Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-C object typedef yields strange @encode and breaks KVC

Consider the following code:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

typedef NSString* MyStringRef;
typedef NSString MyString;

@interface ClassA : NSObject
@property (nonatomic, copy) MyStringRef stringA;
@property (nonatomic, copy) MyString *stringB;
@end

@implementation ClassA
@synthesize stringA = _stringA;
@synthesize stringB = _stringB;
@end

int main() {
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([ClassA class], &count);
    for (unsigned int i = 0; i < count; i++) {
        Ivar thisIvar = ivars[i];
        NSLog(@"thisIvar = %s, %s", ivar_getName(thisIvar), ivar_getTypeEncoding(thisIvar));
    }

    ClassA *a = [[ClassA alloc] init];
    NSLog(@"Out: %@", [a valueForKey:@"stringA"]);
    NSLog(@"Out: %@", [a valueForKey:@"stringB"]);
}

This is the output:

$ clang --version
Apple clang version 3.1 (tags/Apple/clang-318.0.58) (based on LLVM 3.1svn)
Target: x86_64-apple-darwin11.4.0
Thread model: posix

$ clang -o typedef -fobjc-arc -framework Foundation typedef.m && ./typedef
2012-06-06 20:14:15.881 typedef[37282:707] thisIvar = _stringA, @"NSString"
2012-06-06 20:14:15.884 typedef[37282:707] thisIvar = _stringB, ^{NSString=#}
2012-06-06 20:14:15.885 typedef[37282:707] Out: (null)
2012-06-06 20:14:15.888 typedef[37282:707] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<ClassA 0x7fabe0501480> valueForUndefinedKey:]: this class is not key value coding-compliant for the key stringB.'
*** First throw call stack:
(
    0   CoreFoundation                      0x00007fff835fef56 __exceptionPreprocess + 198
    1   libobjc.A.dylib                     0x00007fff878e5d5e objc_exception_throw + 43
    2   CoreFoundation                      0x00007fff836891b9 -[NSException raise] + 9
    3   Foundation                          0x00007fff83e77703 -[NSObject(NSKeyValueCoding) valueForUndefinedKey:] + 240
    4   Foundation                          0x00007fff83dae38e _NSGetUsingKeyValueGetter + 108
    5   Foundation                          0x00007fff83dae315 -[NSObject(NSKeyValueCoding) valueForKey:] + 392
    6   typedef                             0x000000010e84bc6d main + 317
    7   typedef                             0x000000010e84b9c4 start + 52
)

The question I have here is what is it about Objective-C that causes the typedef NSString MyString to effectively create a struct that contains one variable of type Class and then uses that where I use MyString. e.g. the struct looks like this (after consulting this):

struct NSString {
    Class a;
};

It sort of makes sense but causes the valueForKey: to fail, presumably because it's a struct now, so it can't return that in the same way as objects. Or more precisely, it falls into the "throw exception" part of the search order described in the docs.

I'd just like to understand what it is about the language that causes this to happen and why it can't treat my 2 typedefs in the same way.

like image 350
mattjgalloway Avatar asked Jun 06 '12 19:06

mattjgalloway


2 Answers

I got an answer to this at WWDC 2012. It turns out that this behaviour is expected as is for binary compatibility with GCC.

Seems odd to me that they'd effectively introduce what I would consider a bug to be binary compatible with old GCCs. I hope that this sort of thing will eventually be phased out in favour of a correct compiler.

like image 126
mattjgalloway Avatar answered Nov 14 '22 03:11

mattjgalloway


I can reproduce this, but only with gcc. With clang, I get:

$ clang --version
Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn)
Target: x86_64-apple-darwin10
Thread model: posix
$ ./a.out
2012-06-06 15:16:37.947 a.out[61063:903] thisIvar = _stringA, @"NSString"
2012-06-06 15:16:37.949 a.out[61063:903] thisIvar = _stringB, @"NSString"
2012-06-06 15:16:37.949 a.out[61063:903] Out: (null)
2012-06-06 15:16:37.950 a.out[61063:903] Out: (null)

So, it seems the clang developers agree with you about how it should work.

(I also tried Apple clang version 3.0. Same good result.)

like image 28
Ken Thomases Avatar answered Nov 14 '22 03:11

Ken Thomases