Objective-c Internal for NSArray and NSMutableArray

A very interesting question for all those inside Objective-c ...

So ... NSObject returns the same copy implementation for both the object and the object and class (as I expect). However, NSArray and NSMutableArray not only return different objectAtIndex: implementations objectAtIndex: for the object and class, but each object has a different implementation.

Does anyone know why the following code creates this behavior? ... (At least the implementation of the classes for NSArray and NSMutableArray same :))

 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:)))]); 

Magazine

 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> 
+8
ios objective-c iphone
source share
1 answer

Information about the implementation, all of them. Thus, this is a relatively well-informed bit of hypothesis.

Firstly, you don’t have to jump over such hoops to print a hex value, just do:

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

Note that calling methodForSelector: on the class object returns an IMP for the class method.

Now to the question.

One thing that distinguishes Objective-C from other popular OO languages ​​(but not all) is that class objects are actually instances of a metaclass. This Metaclass, which really does not have a name outside the "metaclass," responds to the NSObject protocol (more or less). This is actually a sorta-kinda derivative of NSObject .

That way, when you get the implementation of certain NSObject selectors for instances and classes, they will be the same. Note that this is what allows the class to be the key in an NSDictionary; Classes can be copied. NSObject copy method simply does return [self retain]; and, of course, retain in the class is no-op, since they [almost always] are statically compiled into binary files as single files. Well, technically, copy calls copyWithZone: which does return [self retain]; (therefore, you must subclass copyWithZone: even if the zones are out of date).

Now, as Hot Licks noted, NS*Array is a cluster of classes whose internal implementation data has changed over the past few releases. It used to be that all instances were bound to NSCFArray , which were configured differently. In later versions, you will see instances of (sic.) __NSArrayI and __NSArrayM , which correspond to immutable and mutable instances. Mostly, more than just going on, but it's pretty typical.

That instance methods for objectAtIndex: differ between two classes, is typical for class clusters. The point of the cluster is to provide a primitive interface with a set of methods implemented in terms of this primitive interface (therefore, the headers are divided between the @interface core and the series of categorical @interfaces ; the categories in the base classes are implemented completely from the point of view of the main API).

Inside there are specific subclasses of public classes within the cluster. These specific subclasses are highly optimized for a specific task - an immutable and mutable array storage, in this case (but there may be many other non-classical subclasses optimized for different purposes) - where subclasses override the advertised API to provide highly optimized versions of various methods.

Thus, what you see through the two classes is a different implementation of objectAtIndex: optimized for mutable and immutable.

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' 

Yeah! Thus, you are requesting an implementation of a method that, when called, blocks the exception "does not recognize this method." Actually:

 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, which will lead to a good error indicating that you tried - through a roundabout way - to use a selector so that the target (the metaclass instance in this case) would not respond to. That's why in the documentation for instanceMethodForSelector: it is indicated that you should use respondsToSelector: earlier in cases where the question may arise whether the target selector implements.

(well, that turned into a larger part of the book than intended ... hopefully still useful!)

+26
source share

All Articles