簡體   English   中英

Objective-c NSArray和NSMutableArray的內部結構

[英]Objective-c Internals for NSArray and NSMutableArray

對於所有進入Objective-c內部的人來說非常有趣的問題......

所以... NSObject為對象和類返回相同的copy實現(正如我所料)。 但是, NSArrayNSMutableArray返回objectAtIndex:不同實現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:)))]);

日志

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>

所有的實施細節。 因此,這是一個相對明智的猜想。

首先,不需要跳過這樣的箍來打印十六進制值,只需:

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

請注意,在類對象上調用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 在更新的版本中,您將看到(sic。) __NSArrayI__NSArrayM實例,它們對應於不可變和可變實例。 大多數情況下 - 還有更多的事情發生,但這很典型。

objectAtIndex:的實例方法在兩個類之間是不同的,這是典型的類集群。 集群的要點是提供一個原始接口,其中包含一些根據該原始接口實現的方法(這就是為什么頭部在核心@interface和一系列分類@interfaces之間划分的原因;基類中的類別完全根據核心API實現。

在內部,集群中公開聲明的類有具體的子類。 這些具體的子類針對特定任務進行了高度優化 - 在這種情況下是不可變的和可變的數組存儲(但是可能有更多的非公共子類針對不同的目的進行了優化) - 其中子類覆蓋廣告API以提供高度各種方法的優化版本。

因此,您在兩個類中看到的是objectAtIndex:不同實現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'

啊哈! 因此,您要求實現一種方法,該方法在調用時會阻止“無法識別此方法”異常。 事實上:

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

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

看起來運行時正在返回一個IMP,它會產生一個很好的錯誤,表明你試過 - 通過環形交換方式 - 使用一個選擇器,目標對象(在這種情況下是一個元類實例)沒有響應。 這就是為什么instanceMethodForSelector:的文檔說明你應該使用respondsToSelector:在可能存在關於目標是否實現選擇器的問題的情況下。

(好吧,這變成了一本書而不是預期...希望仍然有用!)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM