Objective-c NSArray和NSMutableArray的内部结构

Eam*_*ney 8 iphone objective-c ios

对于所有进入Objective-c内部的人来说非常有趣的问题......

所以... NSObject返回copy对于对象和类(如我所料)的相同实现.但是,NSArray并且不仅NSMutableArray返回objectAtIndex:对象和类的不同实现,而且每个对象具有不同的实现.

有谁知道为什么跟随代码产生这样的行为?...(至少是类的实现NSArrayNSMutableArray是相同的:))

NSObject *obj = [[[NSObject alloc] init] autorelease];
NSLog(@"NSObject instance %@", [NSValue valueWithPointer:method_getImplementation(class_getInstanceMethod(object_getClass(obj), @selector(copy)))]);
NSLog(@"NSObject class %@", [NSValue valueWithPointer:method_getImplementation(class_getInstanceMethod([NSObject class], @selector(copy)))]);

NSArray *array = [[[NSArray alloc] init] autorelease];
NSLog(@"NSArray instance %@", [NSValue valueWithPointer:method_getImplementation(class_getInstanceMethod(object_getClass(array), @selector(objectAtIndex:)))]);
NSLog(@"NSArray class %@", [NSValue valueWithPointer:method_getImplementation(class_getInstanceMethod([NSArray class], @selector(objectAtIndex:)))]);

NSMutableArray *array1 = [[[NSMutableArray alloc] init] autorelease];
NSLog(@"NSMutableArray instance %@", [NSValue valueWithPointer:method_getImplementation(class_getInstanceMethod(object_getClass(array1), @selector(objectAtIndex:)))]);
NSLog(@"NSMutableArray class %@", [NSValue valueWithPointer:method_getImplementation(class_getInstanceMethod([NSMutableArray class], @selector(objectAtIndex:)))]);
Run Code Online (Sandbox Code Playgroud)

日志

2012-11-06 16:35:22.918 otest[71367:303] NSObject instance <c0fa7200>
2012-11-06 16:35:23.757 otest[71367:303] NSObject class <c0fa7200>
2012-11-06 16:35:30.348 otest[71367:303] NSArray instance <809a9b00>
2012-11-06 16:35:31.121 otest[71367:303] NSArray class <70bfa700>
2012-11-06 16:35:33.854 otest[71367:303] NSMutableArray instance <f05f9a00>
2012-11-06 16:35:34.824 otest[71367:303] NSMutableArray class <70bfa700>
Run Code Online (Sandbox Code Playgroud)

bbu*_*bum 26

所有的实施细节.因此,这是一个相对明智的猜想.

首先,不需要跳过这样的箍来打印十六进制值,只需:

NSLog(@"imp: %p", [NSObject instanceMethodForSelector:@selector(...)]);
Run Code Online (Sandbox Code Playgroud)

请注意,methodForSelector:对类对象进行调用会返回类方法的IMP.

现在,关于手头的问题.

将Objective-C与其他流行的OO语言(但不是全部)区别开来的一件事是Class对象实际上是Metaclass的实例.那个Metaclass - 它真的没有"metaclass"之外的名字 - 碰巧响应NSObject协议(或多或少).事实上,它有点像一种衍生物NSObject.

因此,当您NSObject为实例和类获得某些选择器的实现时,它们将是相同的.请注意,这是允许Class成为NSDictionary中的键的原因; 可以复制类. NSObjectcopy方法只是做return [self retain];,当然,retain一类是一个空操作,因为它们是[几乎总是]静态编译成二进制作为单身.嗯,从技术上讲,这样做的copy调用(这就是为什么你必须继承子类,即使不推荐使用区域).copyWithZone:return [self retain];copyWithZone:

现在,正如Hot Licks指出的那样,NS*Array是一个类集群,其内部实现细节在最近几个版本中已经发生了变化.过去,所有实例都被桥接到NSCFArray不同的配置.在更新的版本中,您将看到(原文如此)__NSArrayI__NSArrayM与不可变和可变实例相对应的实例.大多数情况下 - 还有更多的事情发生,但这很典型.

实例方法objectAtIndex:在两个类之间是不同的是典型的类集群.集群的要点是提供一个原始接口,其中包含一些根据该原始接口实现的方法(这就是为什么标题在核心@interface和一系列分类之间划分的原因@interfaces;基类中的类别完全在核心API的条款).

在内部,集群中公开声明的类有具体的子类.这些具体的子类针对特定任务进行了高度优化 - 在这种情况下是不可变的和可变的数组存储(但是可能有更多的非公共子类针对不同的目的进行了优化) - 其中子类覆盖广告API以提供高度各种方法的优化版本.

因此,您在两个类中看到的是objectAtIndex:针对可变案例和不可变案例进行优化的不同实现.

现在,为什么类方法一样?好问题.我们试试吧:

((void(*)(id,SEL,int))[[NSArray class] methodForSelector: @selector(objectAtIndex:)])([NSArray class], @selector(objectAtIndex:), 0);


2012-11-06 09:18:23.842 asdfasdf[17773:303] *** Terminating app due to uncaught
   exception 'NSInvalidArgumentException', reason: '+[NSArray objectAtIndex:]:
   unrecognized selector sent to class 0x7fff7563b1d0'
Run Code Online (Sandbox Code Playgroud)

啊哈!因此,您要求实现一种方法,该方法在调用时会阻止"无法识别此方法"异常.事实上:

NSLog(@"%@", 
 [NSArray class] respondsToSelector:@selector(objectAtIndex:)] ? @"YES" : @"NO");

2012-11-06 09:24:31.698 asdfasdf[17839:303] NO
Run Code Online (Sandbox Code Playgroud)

看起来运行时正在返回一个IMP,它会产生一个很好的错误,表明你试过 - 通过环形交换方式 - 使用一个选择器,目标对象(在这种情况下是一个元类实例)没有响应.这就是为什么在可能存在关于目标是否实现选择器的问题的情况下instanceMethodForSelector:您应该使用的状态的文档respondsToSelector:.

(好吧,这变成了一本书而不是预期...希望仍然有用!)