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.
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.
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
.
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