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

196 Views Asked by At

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"?

6

There are 6 best solutions below

6
On BEST ANSWER

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

1
On

In your software development, the only classes you worry about are NSArray and NSMutableArray. Behind the scenes, there are many different classes. For example, a singleton class for empty arrays. A class for arrays with one array element. All saves memory, because people use tons and tons of trivial arrays.

0
On

__NSArrayI is a code-word for an immutable array - that is, a "regular" NSArray which you cannot change.

__NSArrayM is a code-word for a mutable array - that is, NSMutableArray. In NSMutableArray, you can add and remove items.

0
On

__NSArrayI refers to an immutable version of the array, which cannot be changed after initialization.

0
On

__NSArrayI means the immutable form of NSArray. And there is also the __NSArrayM, which is the mutable form. What immutable means is that you cannot change it, the objects inside this array cannot be changed. And for mutable, you can change the elements. U can check out this post also. I think this also answers your question. What is __NSArrayI and __NSArrayM? How to convert to NSArray?

0
On

__NSArrayI is a private class for an immutable NSArray. Also there is __NSArrayM which is a private class for a mutable array. Both are subclasses of NSArray

+ arrayWithObjects: returns a __NSArrayI there is nothing you can do about it as this is private API you cannot use for app store distribution.

If you want to check that your object is a NSArray you can use

if ([anArray isKindOfClass:[NSArray class]]){
     //blablabla
}