objective-cnsarrayclass-cluster

I created an instance of NSArray, whereas whose class is not NSArray but __NSArrayI?


I have the following code:

id anArray = [NSArray arrayWithObjects:@1, @2, nil];
NSLog(@"anArrayClass - %@", [anArray class]);
NSLog(@"NSArrayClass - %@", [NSArray class]);

And I expect both output are NSArray, however the output turns out to be:

2016-08-18 21:08:53.628 TestUse[9279:939745] anArrayClass - __NSArrayI
2016-08-18 21:08:53.629 TestUse[9279:939745] NSArrayClass - NSArray

Then I create a test class called CAJTestClass and create an instance of that class:

id testInstance = [CAJTestClass new];
NSLog(@"testInstanceClass - %@", [testInstance class]);
NSLog(@"cajTestClass - %@", [CAJTestClass class]);

This time the output becomes:

2016-08-18 21:08:53.629 TestUse[9279:939745] testInstanceClass - CAJTestClass
2016-08-18 21:08:53.629 TestUse[9279:939745] cajTestClass - CAJTestClass

This time the result is what I expected. But why would [anArray class] to be a __NSArrayI?
An explanation from "Effective Objective-C" is that NSArray is a part of a "class cluster"(which I think is a series of classes that have inheriting relationships). But CAJTestClass is also a subclass of NSObject. Am I wrong?

EDIT: Thanks for all your answers. But my question is exactly why I get different result in this two cases if it should contribute to the affairs of "class cluster"?


Solution

  • EDIT: Thanks for all your answers. But my question is exactly why I get different result in this two cases if it should contribute to the affairs of "class cluster"?

    Because the test code is completely different. You're calling an NSArray method that returns a subclass of NSArray, but you're calling [CAJTestClass new], which returns CAJTestClass itself. If you make them be the same, then you get the same results:

    @interface CAJTestClass : NSObject
    + (instancetype)testClassWithMagic;
    @end
    
    @interface __MagicTestSubclass : CAJTestClass
    @end
    
    @implementation CAJTestClass
    
    + (instancetype)testClassWithMagic {
        return [__MagicTestSubclass new];
    }
    @end
    
    @implementation __MagicTestSubclass
    @end
    

    Now using your test code:

        id testInstance = [CAJTestClass testClassWithMagic];
        NSLog(@"testInstanceClass - %@", [testInstance class]);
        NSLog(@"cajTestClass - %@", [CAJTestClass class]);
    
    2016-08-18 09:57:15.126 test[72004:47882338] testInstanceClass - __MagicTestSubclass
    2016-08-18 09:57:15.127 test[72004:47882338] cajTestClass - CAJTestClass
    

    rmaddy raises the possibility that you have a different question and he may be correct, so I'll answer that one as well.

    [anArray class] is the result of passing the -class message to the instance anArray. The usual thing for an instance to do when it receives the -class message is to return the specific class it was initialized as (its specific subclass). This is not universal (KVO classes intentionally break this rule and it's even possible to change classes at runtime), but it is the common approach. This is the isa pointer in the struct that tells the dispatcher which set of methods to use. So you get the actual as-instantiated class object (__NSArrayI).

    [NSArray class] is the result of passing the +class message to the class object NSArray. The usual thing for a class object to is return self (I don't know any classes that violate that rule; it may not be legal to violate that rule). So you get the class you passed the message to (NSArray).