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.
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.
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With