Very interesting question for all those into Objective-c internals...
So... the NSObject
returns the same implementation of copy
for both and object and a class (As I expect). However, NSArray
and NSMutableArray
return not just different implementations of objectAtIndex:
for an object and class but each object has a different implementation.
Does anyone know why the follow code produces such behaviour?... (Atleast the class implementations for NSArray
and NSMutableArray
are the 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:)))]);
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>
Creating NSArray Objects Using Array Literals In addition to the provided initializers, such as initWithObjects: , you can create an NSArray object using an array literal. In Objective-C, the compiler generates code that makes an underlying call to the init(objects:count:) method.
The primary difference between NSArray and NSMutableArray is that a mutable array can be changed/modified after it has been allocated and initialized, whereas an immutable array, NSArray , cannot.
NSMutableArray and NSArray both are build on CFArray , performance/complexity should be same. The access time for a value in the array is guaranteed to be at worst O(lg N) for any implementation, current and future, but will often be O(1) (constant time).
NSArray & NSMutableArray NSArray is used to hold an immutable array of objects and the NSMutableArray is used to hold an mutable array of objects.
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.
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. That Metaclass -- which really doesn't have a name beyond "metaclass" -- happens to respond to the NSObject
protocol (more or less). In fact, it is sorta-kinda a derivative of NSObject
.
Thus, when you get the implementation for certain selectors on NSObject
for instances and classes, they'll oft be the same. Note that this is what allows a Class to be a key in an NSDictionary; Classes can be copied. NSObject
's copy
method just does 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. Well, technically, copy
calls copyWithZone:
that does return [self retain];
(which is why you have to subclass copyWithZone:
even though zones are deprecated).
Now, as Hot Licks pointed out, NS*Array
is a class cluster whose internal implementation details have changed over the last few releases. It used to be that all instances were all bridged to NSCFArray
that was configured differently. In more recent releases, you'll see (sic.) __NSArrayI
and __NSArrayM
instances that correspond to immutable and mutable instances. 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. 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).
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.
Thus, what you are seeing across the two classes are different implementations of objectAtIndex:
optimized for the mutable vs. the immutable case.
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. 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.
(well, that turned into more of a book than intended... hopefully still useful!)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With