Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

isKindOfClass Behavior

Tags:

objective-c

Can someone explain why isKindOfClass returns different results depending on how the instance was created?

@interface BaseClass
...

@interface DerivedClassA : BaseClass
...

DerivedClassA *instance = [[DerivedClassA alloc] init];

[instance isKindOfClass:[BaseClass class]];  // yields YES

Class c = NSClassFromString(@"DerivedClassA");
id instance = [[c alloc] init];

[instance isKindOfClass:[BaseClass class]];  // yields NO

Everything else I can glean from the two types in the debugger is identical. I can even compare both results of NSStringFromClass([instance superclass]) and they are equal.

I must be missing something simple.

Updated Code

This is the unit test code.

LightingUnit *u1 = [[LightingUnit alloc] init];

STAssertTrue([u1 isKindOfClass:[ModelBase class]], @"should be derived from base");

Class uc = NSClassFromString(@"LightingUnit");
id u2 = [[uc alloc] init];

STAssertTrue([u2 isKindOfClass:[ModelBase class]], @"should be derived from base");

Here are the class definitions.

@interface ModelBase : NSObject

@property (readonly) NSString *__type;

- (id)initWithDictionary:(NSDictionary *)dictionary;

- (NSMutableDictionary *)dictionary;

@end

@interface LightingUnit : ModelBase

@property (strong, nonatomic) NSString *name;
@property NSInteger unitId;

@end

Possible Answer

When I run this logic outside of the test environment its works without issue. Obviously the only difference is removing the STAssertTrue statements and replacing them with my conditions. In this case they both return YES. I even tried to create a simplified example (no ivars in base or derived) and it fails in test but works in standard runtime.

Any ideas why this might only be an issue when testing? Is my test target missing something?

Solved

I had included the .m files in the testing targets compile sources. Once removed this started behaving as expected. Thanks to this post for helping me resolve this.

like image 602
Brian Mahloch Avatar asked Jan 23 '13 01:01

Brian Mahloch


2 Answers

I added your code in a test app of mine. I had to change the name of the two instance variables, afterwards the compiler gave me a warning ("Class method '+alloc' not found"), and the program crashed when I ran it.

I then changed this line:

@interface BaseClass : NSObject

i.e. I derived BaseClass from NSObject. No more compiler warning and the code runs as expected, i.e. the second isKindOfClass returns YES.

I don't know if the missing NSObject is what causes your problem. If it's not then someone else has to step in. If it is, I wonder how you were able to run your code at all.

like image 58
herzbube Avatar answered Sep 28 '22 00:09

herzbube


Whether or not BaseClass inherits from NSObject will change the outcome of this code. Here are the two possibilities:


@interface BaseClass

If you have defined BaseClass without inheriting from NSObject, then quite frankly I'm surprised the compiler let you get away with setting up your instance variable. Unless you defined your own +alloc and -init methods on BaseClass (which is possible), then the first line of your test won't work at all. If you did define an appropriate +alloc and -init method, you'll still run into trouble with -isKindOfClass: as you'll need to define that yourself as well. So basically this won't work, let's look at the next option...


@interface BaseClass : NSObject

The first line of your test (creating instance) should work fine.

[instance isKindOfClass:[BaseClass class]] returns the appropriate value, YES.

Your class c initialisation should work correctly.

I suspect there is something omitted here because the final line should return YES with the code you posted (assuming BaseClass inherits from NSObject)... The final line should return YES. I suspect what may have happened is instance has somehow wound up containing nil through incorrect initialisation/typo/etc. In this case, sending it a message will just return nil, and thus sending -isKindOfClass will yield NO.

like image 22
Ephemera Avatar answered Sep 28 '22 00:09

Ephemera