简体   繁体   English

Objective-c NSArray和NSMutableArray的内部结构

[英]Objective-c Internals for NSArray and NSMutableArray

Very interesting question for all those into Objective-c internals... 对于所有进入Objective-c内部的人来说非常有趣的问题......

So... the NSObject returns the same implementation of copy for both and object and a class (As I expect). 所以... NSObject为对象和类返回相同的copy实现(正如我所料)。 However, NSArray and NSMutableArray return not just different implementations of objectAtIndex: for an object and class but each object has a different implementation. 但是, NSArrayNSMutableArray返回objectAtIndex:不同实现objectAtIndex:对于对象和类,但每个对象具有不同的实现。

Does anyone know why the follow code produces such behaviour?... (Atleast the class implementations for NSArray and NSMutableArray are the same :) ) 有谁知道为什么跟随代码产生这样的行为?...(至少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:)))]);

The Log 日志

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>

Implementation details, all of 'em. 所有的实施细节。 Thus, this is a relatively well informed bit of conjecture. 因此,这是一个相对明智的猜想。

First, no need to jump through such hoops to print a hex value, just do: 首先,不需要跳过这样的箍来打印十六进制值,只需:

NSLog(@"imp: %p", [NSObject instanceMethodForSelector:@selector(...)]);

Note that invoking methodForSelector: on a class object returns the IMP for a class method. 请注意,在类对象上调用methodForSelector:返回类方法的IMP。

Now, on to the question at hand. 现在,关于手头的问题。

One thing that differentiates Objective-C from other popular OO languages (but not all), is that Class objects are actually instances of a Metaclass. 将Objective-C与其他流行的OO语言(但不是全部)区别开来的一件事是Class对象实际上是Metaclass的实例。 That Metaclass -- which really doesn't have a name beyond "metaclass" -- happens to respond to the NSObject protocol (more or less). 那个Metaclass - 它真的没有“metaclass”之外的名字 - 恰好响应了NSObject协议(或多或少)。 In fact, it is sorta-kinda a derivative of NSObject . 事实上,它有点像NSObject的衍生物。

Thus, when you get the implementation for certain selectors on NSObject for instances and classes, they'll oft be the same. 因此,当您在NSObject获取实例和类的某些选择器的实现时,它们将是相同的。 Note that this is what allows a Class to be a key in an NSDictionary; 请注意,这是允许Class成为NSDictionary中的键的原因; Classes can be copied. 可以复制类。 NSObject 's copy method just does return [self retain]; NSObjectcopy方法只是return [self retain]; and, of course, retain on a class is a no-op since they are [almost always] statically compiled into the binary as singletons. 当然, retain在一个类是一个无操作,因为它们[几乎总是]静态编译成二进制作为单例。 Well, technically, copy calls copyWithZone: that does return [self retain]; 好吧,从技术上讲, copy调用copyWithZone:它确实return [self retain]; (which is why you have to subclass copyWithZone: even though zones are deprecated). (这就是你必须copyWithZone:即使不推荐使用区域)。

Now, as Hot Licks pointed out, NS*Array is a class cluster whose internal implementation details have changed over the last few releases. 现在,正如Hot Licks指出的那样, NS*Array是一个类集群,其内部实现细节在最近几个版本中已经发生了变化。 It used to be that all instances were all bridged to NSCFArray that was configured differently. 过去,所有实例都被桥接到不同配置的NSCFArray In more recent releases, you'll see (sic.) __NSArrayI and __NSArrayM instances that correspond to immutable and mutable instances. 在更新的版本中,您将看到(sic。) __NSArrayI__NSArrayM实例,它们对应于不可变和可变实例。 Mostly -- there is more going on than just that, but that is pretty typical. 大多数情况下 - 还有更多的事情发生,但这很典型。

That the instance methods for objectAtIndex: are different between the two classes is typical of class clusters. objectAtIndex:的实例方法在两个类之间是不同的,这是典型的类集群。 The point of a cluster is to provide a primitive interface with a bunch of methods implemented in terms of that primitive interface (which is why the headers are divided between the core @interface and a series of categorical @interfaces ; the categories in the base classes are implemented entirely in terms of the core API). 集群的要点是提供一个原始接口,其中包含一些根据该原始接口实现的方法(这就是为什么头部在核心@interface和一系列分类@interfaces之间划分的原因;基类中的类别完全根据核心API实现。

Internally, there are concrete subclasses of the publicly declared classes within the cluster. 在内部,集群中公开声明的类有具体的子类。 Those concrete subclasses are highly optimized to a particular task -- immutable vs. mutable array storage, in this case (but there may be many more non-public subclasses optimized to different purposes) -- where the subclasses override the advertised API to provide highly optimized versions of the various methods. 这些具体的子类针对特定任务进行了高度优化 - 在这种情况下是不可变的和可变的数组存储(但是可能有更多的非公共子类针对不同的目的进行了优化) - 其中子类覆盖广告API以提供高度各种方法的优化版本。

Thus, what you are seeing across the two classes are different implementations of objectAtIndex: optimized for the mutable vs. the immutable case. 因此,您在两个类中看到的是objectAtIndex:不同实现objectAtIndex:针对可变案例和不可变案例进行了优化。

Now, why are the class methods the same? 现在,为什么类方法一样? Good question. 好问题。 Let's try calling it: 我们试试吧:

((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'

Aha! 啊哈! So, you are asking for the implementation of a method that, when invoked, barfs up a "does not recognize this method" exception. 因此,您要求实现一种方法,该方法在调用时会阻止“无法识别此方法”异常。 In fact: 事实上:

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

2012-11-06 09:24:31.698 asdfasdf[17839:303] NO

It looks like the runtime is returning an IMP that will barf up a nice error indicating that you tried -- through roundabout means -- to use a selector that the targeted object (a metaclass instance, in this case) does not respond to. 看起来运行时正在返回一个IMP,它会产生一个很好的错误,表明你试过 - 通过环形交换方式 - 使用一个选择器,目标对象(在这种情况下是一个元类实例)没有响应。 Which is why the documentation for instanceMethodForSelector: states that you should use respondsToSelector: prior in cases where there may be some question about whether the target implements the selector. 这就是为什么instanceMethodForSelector:的文档说明你应该使用respondsToSelector:在可能存在关于目标是否实现选择器的问题的情况下。

(well, that turned into more of a book than intended... hopefully still useful!) (好吧,这变成了一本书而不是预期...希望仍然有用!)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM