[英]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. 但是,
NSArray
和NSMutableArray
返回objectAtIndex:
不同实现objectAtIndex:
对于对象和类,但每个对象具有不同的实现。
Does anyone know why the follow code produces such behaviour?... (Atleast the class implementations for NSArray
and NSMutableArray
are the same :) ) 有谁知道为什么跟随代码产生这样的行为?...(至少
NSArray
和NSMutableArray
的类实现是相同的:))
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];
NSObject
的copy
方法只是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.