運(yùn)行時(shí)-RunTime (官方文檔)

iOS 運(yùn)行時(shí)

Objective-C語(yǔ)言進(jìn)可能將許多決策從編譯和鏈接延緩到運(yùn)行時(shí)。它盡可能的動(dòng)態(tài)處理事務(wù)溶耘。這意味著Objective-C不僅需要編譯器還需要執(zhí)行編譯代碼的運(yùn)行時(shí)系統(tǒng)澜倦。運(yùn)行時(shí)系統(tǒng)充當(dāng)Objective-C的一種操作系統(tǒng)桩卵,使之正常工作高职。

Objective-C 程序與運(yùn)行時(shí)系統(tǒng)的交互主要在三個(gè)不同的層次

通過(guò)Objective-C 源代碼怔锌;通過(guò)基礎(chǔ)框架中NSObject類(lèi)中定義的方法寥粹;通過(guò)直接調(diào)用運(yùn)行時(shí)函數(shù)。

  • Objective-C 源代碼
    在大多數(shù)情況下埃元,運(yùn)行時(shí)系統(tǒng)自動(dòng)在后臺(tái)工作涝涤。只通過(guò)編寫(xiě)和編譯Objective-C源代碼才會(huì)使用到。
    當(dāng)編譯包含Objective-C類(lèi)和方法的代碼岛杀,編譯器會(huì)創(chuàng)建數(shù)據(jù)結(jié)構(gòu)和函數(shù)調(diào)用阔拳,實(shí)現(xiàn)語(yǔ)言的動(dòng)態(tài)特性。數(shù)據(jù)結(jié)構(gòu)捕獲類(lèi)和分類(lèi)定義以及協(xié)議中聲明的信息楞件,其中包括Objective-C編程語(yǔ)言中定義類(lèi)和協(xié)議中討論的類(lèi)和協(xié)議衫生,也包括方法選擇器裳瘪、實(shí)例變量模板和從源碼中提取的其他信息土浸。運(yùn)行時(shí)函數(shù)的主要功能是發(fā)送消息。被源碼消息表達(dá)式調(diào)用彭羹。
  • NSObject 方法
    Cocoa中大多數(shù)對(duì)象都是NSObject的之類(lèi)黄伊,所以大多數(shù)對(duì)象都繼承NSObject定義的方法。(NSProxy類(lèi)是個(gè)例外派殷,更多信息參見(jiàn)消息轉(zhuǎn)發(fā)还最。)其方法因此為每個(gè)實(shí)例每個(gè)類(lèi)對(duì)象建立行為。然而毡惜,在一些情況下拓轻,NSObject類(lèi)只定義了一個(gè)模板,告知如何完成经伙,并沒(méi)有提供必要的代碼扶叉。
    例如,NSObject類(lèi)定義了一個(gè)描述實(shí)例方法,該方法返回一個(gè)字符串描述類(lèi)的內(nèi)容枣氧。這主要用于調(diào)試溢十,GDB打印對(duì)象命令打印該方法返回的字符串。NSObject方法的實(shí)現(xiàn)不知道類(lèi)包含的內(nèi)容达吞,所以它返回的是一個(gè)字符串對(duì)象的名稱(chēng)和地址张弛。NSObject的之類(lèi)可以實(shí)現(xiàn)這個(gè)方法并返回更多詳情。例如酪劫,基礎(chǔ)類(lèi)NSArray返回一個(gè)列表吞鸭,包含對(duì)象的表述。
    NSObject的一些方法可以簡(jiǎn)單的查詢(xún)運(yùn)行時(shí)系統(tǒng)信息覆糟。這些方法運(yùn)行對(duì)著執(zhí)行自省瞒大。例子中的這種方法是類(lèi)放,訪問(wèn)一個(gè)對(duì)象來(lái)確定它的類(lèi)搪桂;isKindOfClass:和isMemberOfClass:測(cè)試對(duì)象在繼承層次結(jié)構(gòu)中的位置透敌;respondsToSelector:表明一個(gè)對(duì)象是否可以接收特定消息;conformsToProtocol:表明一個(gè)對(duì)象是否要求實(shí)現(xiàn)特定協(xié)議中定義的方法踢械;methodForSelector:提供方法實(shí)現(xiàn)的地址酗电。類(lèi)似這樣的方法給對(duì)象自我反省的能力。
  • 運(yùn)行時(shí)函數(shù)
    運(yùn)行時(shí)系統(tǒng)是一個(gè)動(dòng)態(tài)共享庫(kù)内列,包含公共接口組成的一組函數(shù)和數(shù)據(jù)結(jié)構(gòu)撵术,其頭文件位于目錄/usr/include/objc。這些函數(shù)允許使用純C復(fù)制當(dāng)編寫(xiě)Objective-C代碼時(shí)編譯器生成的代碼话瞧。通過(guò)NSObject類(lèi)方法導(dǎo)出其他形式的基礎(chǔ)功能嫩与。這些函數(shù)可以開(kāi)發(fā)運(yùn)行時(shí)系統(tǒng)的其他接口和產(chǎn)生可以擴(kuò)大開(kāi)發(fā)環(huán)境的工具,Objective-C編程中不需要他們交排。然而划滋,一些運(yùn)行時(shí)函數(shù)有時(shí)候在編寫(xiě)Objective-C程序時(shí)很有用。所有的這些函數(shù)在Objective-C編程引用中有說(shuō)明埃篓。

消息傳遞

將消息表達(dá)式轉(zhuǎn)換成objc_msgSend函數(shù)調(diào)用处坪,以及如何通過(guò)名字引用方法。然后解釋如何利用objc_msgSend以及如何避免動(dòng)態(tài)綁定

objc_msgSend函數(shù)

  • 在Objective-C中架专,直到運(yùn)行時(shí)同窘,消息才會(huì)綁定到方法的實(shí)現(xiàn)。編譯器才會(huì)轉(zhuǎn)換消息表達(dá)式部脚,

    [receiver message]  
    
    
  • 調(diào)用消息傳遞函數(shù)objc_msgSend想邦。這個(gè)函數(shù)需要接收者和消息中提到的方法名即方法選擇器作為它的兩個(gè)主要參數(shù):

    objc_msgSend(receiver, selector)  
    
    
  • 消息中傳入的任何參數(shù)都可以在objc_msgSend處理:

    objc_msgSend(receiver, selector, arg1, arg2, ...)  
    
    

消息傳遞函數(shù)支持動(dòng)態(tài)綁定:

  • 首先,獲取選擇器指向的程序(方法實(shí)現(xiàn))委刘。因?yàn)橄嗤姆椒梢员徊煌念?lèi)分別實(shí)現(xiàn)丧没,獲取的具體程序取決于接收器的類(lèi)服傍。
  • 然后調(diào)用程序,通過(guò)傳遞接收對(duì)象(數(shù)據(jù)指針)以及方法中指定的任何參數(shù)骂铁。
  • 最后吹零,它傳遞程序返回值作為自己的返回值。

消息傳遞的關(guān)鍵在于編譯器編譯每個(gè)類(lèi)和對(duì)象的結(jié)構(gòu)拉庵。每個(gè)類(lèi)結(jié)構(gòu)包括這兩個(gè)基本要素

  • 指向父類(lèi)的指針
  • 調(diào)度表灿椅。這個(gè)表的記錄可以將方法選擇器與指定類(lèi)方法的地址關(guān)聯(lián)。setOrigin:: 方法的選擇器與setOrigin::地址(程序?qū)崿F(xiàn))有關(guān)钞支,display 方法的選擇器與的display 地址有關(guān)怔鳖,等等

當(dāng)創(chuàng)建一個(gè)新對(duì)象骤竹,會(huì)分配內(nèi)存并初始化實(shí)例變量瓤荔。首先兜看,對(duì)象變量是一個(gè)指向類(lèi)結(jié)構(gòu)的指針。該指針撼嗓,稱(chēng)為isa柬采,通過(guò)類(lèi),對(duì)象可以訪問(wèn)該類(lèi)和該對(duì)象繼承的所有類(lèi)且警。

注意:isa指針雖然不是語(yǔ)言嚴(yán)格意義上的一部分粉捻,但是是使用Objective-C運(yùn)行時(shí)系統(tǒng)所需的一個(gè)對(duì)象。一個(gè)對(duì)象須“等效于”結(jié)構(gòu)定義中的struct objc_object(定義于objc/objc.h)斑芜。然而肩刃,很少需要?jiǎng)?chuàng)建自己的根對(duì)象和繼承自NSObject 或NSProxy 的對(duì)象,自動(dòng)有isa變量杏头。

當(dāng)一個(gè)消息發(fā)送到一個(gè)對(duì)象盈包,消息傳遞函數(shù)遵循對(duì)象的isa指針,該指針指向類(lèi)結(jié)構(gòu)醇王,并在dispatch表中查找方法選擇器呢燥。如果不能找到選擇器,objc_msgSend則遵循指向父類(lèi)的指針并試圖在dispatch表找到選擇器厦画。一直找不到選擇器疮茄,objc_msgSend將一直查找類(lèi)的層次結(jié)構(gòu),直到NSObject類(lèi)根暑。一旦定位到選擇器,函數(shù)將調(diào)用表中的方法徙邻,并將其傳遞到接收對(duì)象的數(shù)據(jù)結(jié)構(gòu)排嫌。
運(yùn)行時(shí)選擇以這種方式實(shí)現(xiàn)方法$掷纾或者以面向?qū)ο缶幊绦g(shù)語(yǔ)來(lái)說(shuō)淳地,該方法是動(dòng)態(tài)綁定到消息怖糊。
為了加快消息傳遞過(guò)程,運(yùn)行時(shí)系統(tǒng)緩存使用的方法的選擇器和地址颇象。每個(gè)類(lèi)有一個(gè)單獨(dú)的緩存伍伤,可以包含繼承方法和類(lèi)中定義方法的選擇器。在搜索dispatch表之前遣钳,消息傳遞程序首先檢查接收對(duì)象類(lèi)(理論上扰魂,是有可能再次使用的方法)的緩存。如果方法選擇器在緩存中蕴茴,消息傳遞稍微比函數(shù)調(diào)用慢劝评。一旦一個(gè)程序運(yùn)行足夠長(zhǎng)時(shí)間來(lái)“熱身”緩存,幾乎所有發(fā)送的消息都能找到緩存方法倦淀。在程序運(yùn)行時(shí)蒋畜,緩存能動(dòng)態(tài)適應(yīng)新消息。

使用隱式參數(shù)

當(dāng)objc_msgSend發(fā)現(xiàn)實(shí)現(xiàn)方法的程序撞叽,它調(diào)用程序姻成,并傳遞消息中所有的參數(shù)。也傳遞兩個(gè)隱藏參數(shù)到程序:

  • 接收對(duì)象
  • 方法選擇器

這些參數(shù)為每個(gè)方法實(shí)現(xiàn)提供明確信息愿棋,這些信息關(guān)于調(diào)用它們的消息表達(dá)式佣渴。它們被認(rèn)為是“隱藏”的,因?yàn)榉椒ǘx代碼中未聲明它們初斑。當(dāng)編譯代碼時(shí)辛润,它們插入到實(shí)現(xiàn)中。
盡管這些參數(shù)沒(méi)有顯式的聲明见秤,源代碼仍然可以引用它們(就像它可以引用接收對(duì)象的實(shí)例變量)砂竖。方法引用接收對(duì)象作為self,以及自己的選擇器作為_(kāi)cmd鹃答。在下面的例子中乎澄,_cmd引用strange 方法的選擇器,self引用接收一個(gè)strange 消息的對(duì)象测摔。

 - strange  
 {  
    id  target = getTheReceiver();  
    SEL method = getTheMethod();  
   //如果調(diào)用的是自己 置济,就執(zhí)行自己,如果不是 锋八,就繼續(xù)傳遞
    if ( target == self || method == _cmd )  
        return nil;  
    return [target performSelector:method];  
 }  

Self對(duì)兩個(gè)參數(shù)更加有用浙于。實(shí)際上,接收對(duì)象的實(shí)例變量可用于方法定義挟纱。

獲取方法地址

避免動(dòng)態(tài)綁定的唯一方法是獲取方法的地址并且直接調(diào)用它羞酗,就好像它是個(gè)函數(shù)。當(dāng)一個(gè)特定的方法多次連續(xù)執(zhí)行紊服,并且你希望每次執(zhí)行該方法時(shí)避免消息傳遞開(kāi)銷(xiāo)檀轨,在這種極少數(shù)的情況下胸竞,該方法可行。
NSObject類(lèi)中定義一個(gè)methodForSelector:方法参萄,可以訪問(wèn)指向?qū)崿F(xiàn)方法程序的指針卫枝,然后使用指針調(diào)用該程序。methodForSelector:指針的返回值必須指向合適的函數(shù)類(lèi)型讹挎。必須包含返回值和參數(shù)類(lèi)型校赤。
下面的例子展示了程序如何實(shí)現(xiàn)setFilled: 方法:

void (*setter)(id, SEL, BOOL);  
int i;  
   
setter = (void (*)(id, SEL, BOOL))[target  
    methodForSelector:@selector(setFilled:)];  
for ( i = 0 ; i < 1000 ; i++ )  
    setter(targetList[i], @selector(setFilled:), YES);  

前兩個(gè)參數(shù)傳遞給接收對(duì)象(self)的程序和方法選擇器(_cmd)。這些參數(shù)在方法語(yǔ)法中是隱藏的淤袜,但當(dāng)該方法當(dāng)成函數(shù)調(diào)用時(shí)痒谴,必須是顯式的。
使用methodForSelector:方法避免動(dòng)態(tài)綁定節(jié)省了消息轉(zhuǎn)發(fā)所需的大部分時(shí)間铡羡。然而积蔚,只有在特定消息重復(fù)多次的情況下,如上面的for循環(huán)烦周,節(jié)省時(shí)間才會(huì)有重要意義尽爆。
注意:Cocoa運(yùn)行時(shí)系統(tǒng)提供methodForSelector:方法,該方法并不是Objective-C 語(yǔ)言本身的特性读慎。

動(dòng)態(tài)方法解析

如何動(dòng)態(tài)的提供一個(gè)方法的實(shí)現(xiàn)漱贱。

在有些情況下,需要?jiǎng)討B(tài)的提供一個(gè)方法的實(shí)現(xiàn)夭委。例如幅狮,Objective-C 聲明的屬性特征(見(jiàn)Objective-C 編程語(yǔ)言中的聲明屬性)包含@dynamic指令:

@dynamic propertyName;

它告訴編譯器,將動(dòng)態(tài)的提供該方法與屬性株灸。
可以實(shí)現(xiàn)resolveInstanceMethod: 和resolveClassMethod: 方法來(lái)動(dòng)態(tài)的提供一個(gè)給定選擇器的實(shí)例和對(duì)應(yīng)的類(lèi)方法提供實(shí)現(xiàn)崇摄。
一個(gè)Objective-C 方法僅僅是一個(gè)至少有兩個(gè)參數(shù)self和_cmd的C函數(shù)』派眨可以添加在類(lèi)中添加一個(gè)函數(shù)作為一個(gè)使用class_addMethod.函數(shù)的方法逐抑。因此,有以下函數(shù)

  void dynamicMethodIMP(id self, SEL _cmd) {  
    // implementation ....      
  }  

可以動(dòng)態(tài)的將它添加到類(lèi)中作為一個(gè)使用 resolveInstanceMethod: 的方法(稱(chēng)為resolveThisMethodDynamically):

@implementation MyClass  
+ (BOOL)resolveInstanceMethod:(SEL)aSEL  
{  
    if (aSEL == @selector(resolveThisMethodDynamically)) {  
          class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");  
          return YES;  
    }  
    return [super resolveInstanceMethod:aSEL];  
}  
@end  

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
"v@:" v的意思是參數(shù) void 后邊固定@:
參見(jiàn)文檔

動(dòng)態(tài)加載

一個(gè) Objective-C 程序可以在運(yùn)行過(guò)程中加載和鏈接新類(lèi)和分類(lèi)屹蚊。程序中納入新代碼厕氨,在開(kāi)始加載的類(lèi)和類(lèi)別中都可以使用。
動(dòng)態(tài)加載可以用來(lái)做很多不同的事情汹粤。例如命斧,系統(tǒng)偏好設(shè)置應(yīng)用程序中的模塊都是動(dòng)態(tài)加載的。
在Cocoa 環(huán)境中玄括,動(dòng)態(tài)加載通常用來(lái)自定義應(yīng)用程序冯丙。其他人可以編寫(xiě)模塊讓你的程序在運(yùn)行時(shí)加載,類(lèi)似IB加載自定義調(diào)色板和OS X系統(tǒng)設(shè)置應(yīng)用程序加載自定義偏好模塊遭京∥赶В可加載模塊可擴(kuò)展應(yīng)用程序。他們以你允許的方式貢獻(xiàn)代碼哪雕,但是不能自己預(yù)計(jì)和定義船殉。你提供框架,其他人提供代碼斯嚎。
盡管有一個(gè)運(yùn)行時(shí)函數(shù)在Mach-O 文件(在objc/objc-load.h中定義的objc_loadModules)中利虫,執(zhí)行 Objective-C 模塊的動(dòng)態(tài)加載。Cocoa的NSBundle 類(lèi)為動(dòng)態(tài)加載提供了更方便的接口堡僻,這個(gè)接口是面向?qū)ο蟛⑴c相關(guān)服務(wù)結(jié)合糠惫。了解NSBundle 類(lèi)信息和使用,可參閱基礎(chǔ)框架引用中的NSBundle 類(lèi)規(guī)范钉疫。Mach-O 文件信息可查看OS X ABI Mach-O文件格式引用硼讽。

消息轉(zhuǎn)發(fā)

發(fā)送消息到不處理該消息的對(duì)象會(huì)發(fā)生錯(cuò)誤。然而牲阁,在聲明錯(cuò)誤之前固阁,運(yùn)行時(shí)系統(tǒng)給接收對(duì)象第二次機(jī)會(huì)處理該消息。

可以借此實(shí)現(xiàn)多重繼承的功能

如果發(fā)送消息到不處理該消息的對(duì)象城菊,在聲明錯(cuò)誤之前备燃,運(yùn)行時(shí)給該對(duì)象發(fā)送forwardInvocation: 消息,NSInvocation 對(duì)象作為唯一參數(shù)凌唬。NSInvocation 對(duì)象封裝原始消息和需要傳遞的參數(shù)并齐。

可以實(shí)現(xiàn) forwardInvocation:方法,提供一個(gè)默認(rèn)消息響應(yīng)客税,或者以其他方式避免錯(cuò)誤况褪。顧名思義, forwardInvocation:通常用來(lái)將消息轉(zhuǎn)發(fā)給另一個(gè)對(duì)象
為了看到轉(zhuǎn)發(fā)的范圍和目的霎挟,想象以下場(chǎng)景:首先假設(shè)窝剖,你正在設(shè)計(jì)一個(gè)叫做negotiate的對(duì)象可以響應(yīng)消息,你希望它能響應(yīng)另一個(gè)對(duì)象的響應(yīng)酥夭。你可以通過(guò)傳遞negotiate消息到你實(shí)現(xiàn)的negotiate方法中的另一個(gè)對(duì)象赐纱。
更近一步,假設(shè)希望對(duì)象精確的響應(yīng)negotiate 消息熬北,則需要在另一個(gè)類(lèi)中實(shí)現(xiàn)疙描。實(shí)現(xiàn)這個(gè)目標(biāo)的一個(gè)方法是讓類(lèi)繼承其他類(lèi)的方法。然而讶隐,它不可能以這種方式安排事情起胰。也許存在充分的理由為什么你的類(lèi)和實(shí)現(xiàn)negotiate 的類(lèi)在不同分支的繼承層次結(jié)構(gòu)中。

即使類(lèi)不能繼承negotiate 方法巫延,仍然可以通過(guò)實(shí)現(xiàn)一個(gè)版本的方法來(lái)“借用”它效五,該方法只是簡(jiǎn)單的將信息傳遞給另一個(gè)類(lèi)的實(shí)例:

-(id)negotiate  
{   
    if([someOtherObject respondsTo:@selector(negotiate)])  
        return [someOtherObject negotiate];  
    return self;  
}   

這種方式有點(diǎn)麻煩地消,特別是對(duì)象要傳遞大量消息到另一個(gè)對(duì)象。你必須實(shí)現(xiàn)一個(gè)方法來(lái)覆蓋每個(gè)從其他類(lèi)借來(lái)的方法畏妖。此外脉执,這種方法不能處理你不知道的情況。例如戒劫,在寫(xiě)代碼的時(shí)候半夷,你想轉(zhuǎn)發(fā)所有的消息。這取決于運(yùn)行時(shí)的事件迅细,有可能在將來(lái)作為新方法和類(lèi)實(shí)現(xiàn)巫橄。

消息提供第二次機(jī)會(huì),提供了一個(gè)相對(duì)不那么特殊的解決方案茵典,該方案是動(dòng)態(tài)的而非靜態(tài)的湘换。它的工作原理是:當(dāng)一個(gè)對(duì)象因?yàn)闆](méi)有一個(gè)方法匹配消息中的選擇器而無(wú)法響應(yīng)消息時(shí),運(yùn)行時(shí)系統(tǒng)通過(guò)發(fā)送一個(gè)forwardInvocation: 消息通知該對(duì)象敬尺。每個(gè)對(duì)象都從NSObject類(lèi)繼承了forwardInvocation: 方法枚尼。然而,NSObject版本的方法只是簡(jiǎn)單的調(diào)用doesNotRecognizeSelector:砂吞。通過(guò)重寫(xiě)NSObject版本署恍,實(shí)現(xiàn)自己的版本,可以利用forwardInvocation: 消息提供的機(jī)會(huì)轉(zhuǎn)發(fā)消息到其他對(duì)象蜻直。

為了轉(zhuǎn)發(fā)一條消息盯质,forwardInvocation:方法需要做的是:
確定消息要發(fā)送到哪里
發(fā)送消息原來(lái)的參數(shù)到那里
消息可以發(fā)送到 invokeWithTarget: 方法:

- (void)forwardInvocation:(NSInvocation *)anInvocation  
{  
    if ([someOtherObject respondsToSelector:  
            [anInvocation selector]])  
        [anInvocation invokeWithTarget:someOtherObject];  
    else  
        [super forwardInvocation:anInvocation];  
}  

轉(zhuǎn)發(fā)消息的返回值返回給原始發(fā)送者。所有類(lèi)型的返回值都可以傳遞給發(fā)送者概而,包括id呼巷,結(jié)構(gòu),雙精度浮點(diǎn)數(shù)赎瑰。
forwardInvocation: 方法可以作為無(wú)法識(shí)別消息的分發(fā)中心王悍,將消息打包給不同的接收者〔吐或者可以作為中轉(zhuǎn)站压储,發(fā)送所有消息到相同的目的地。它可以把一個(gè)消息轉(zhuǎn)發(fā)給另一個(gè)源譬,或簡(jiǎn)單的“吞咽”一些消息集惋,所有沒(méi)有響應(yīng)也沒(méi)有錯(cuò)誤。forwardInvocation:方法還可以合并幾個(gè)消息到一個(gè)響應(yīng)上踩娘。forwardInvocation:做什么是由系統(tǒng)決定的刮刑。然而,它提供一個(gè)機(jī)會(huì),使得轉(zhuǎn)發(fā)鏈接中的鏈接對(duì)象為程序設(shè)計(jì)提供可能雷绢。
注意:方法只有在他們不調(diào)用名義上接收者的現(xiàn)有方法的情況下才處理消息泛烙。例如,如果你希望你的對(duì)象轉(zhuǎn)發(fā)negotiate 消息到另一個(gè)對(duì)象习寸,則對(duì)象不能有自己的negotiate 方法胶惰。如果是這樣傻工,消息永遠(yuǎn)不會(huì)到forwardInvocation:霞溪。
關(guān)于轉(zhuǎn)發(fā)和調(diào)用的更多信息,參見(jiàn)基礎(chǔ)框架引用中NSInvocation 類(lèi)規(guī)范中捆。

轉(zhuǎn)發(fā)和多重繼承

轉(zhuǎn)發(fā)提供了大部分多重繼承的功能鸯匹。然而,兩者之間有個(gè)重要的區(qū)別:多重繼承在單一對(duì)象上結(jié)合了不同的功能泄伪。它更加強(qiáng)大殴蓬,多層面對(duì)象。另一方面蟋滴,轉(zhuǎn)發(fā)分配單獨(dú)的職責(zé)給不同的對(duì)象染厅。它將問(wèn)題分解為更小的對(duì)象,但是以一種方式將對(duì)象聯(lián)合津函,該方式對(duì)消息發(fā)送者是透明的肖粮。

代理對(duì)象

轉(zhuǎn)發(fā)不僅模仿多重繼承,還可以開(kāi)發(fā)輕量級(jí)對(duì)象代表或“覆蓋”更實(shí)質(zhì)的對(duì)象尔苦。代理代替其他對(duì)象并傳送消息給它涩馆。
Objective-C 編程語(yǔ)言中“遠(yuǎn)程消息傳遞”中描述了該代理。代理負(fù)責(zé)管理消息轉(zhuǎn)發(fā)到遠(yuǎn)程接受者允坚,確保復(fù)制參數(shù)值和恢復(fù)鏈接魂那,等等。但不嘗試做其他稠项。它不與遠(yuǎn)程對(duì)象的功能重復(fù)涯雅,只是簡(jiǎn)單的給遠(yuǎn)程對(duì)象一個(gè)本地地址,該地址可以在另一個(gè)應(yīng)用程序中接收消息展运。
其他類(lèi)型的代理對(duì)象也是可以的活逆。假設(shè),如果有個(gè)對(duì)象操作大量數(shù)據(jù)乐疆,它也許會(huì)創(chuàng)建一個(gè)復(fù)雜的圖片或者讀取磁盤(pán)上的文件內(nèi)容划乖。設(shè)置該對(duì)象可能非常耗時(shí),所以為了簡(jiǎn)單挤土,只有真正需要的時(shí)候或者系統(tǒng)資源暫時(shí)閑置時(shí)使用琴庵。同時(shí),為了保證其他對(duì)象在應(yīng)用程序正常運(yùn)行,該對(duì)象至少需要一個(gè)占位符迷殿。
在這樣的情況下儿礼,你可以首先創(chuàng)建,不是成熟的對(duì)象庆寺,但是時(shí)一個(gè)輕量級(jí)的代理蚊夫。這個(gè)對(duì)象可以做些自己的事情,比如回答數(shù)據(jù)問(wèn)題懦尝,但更多的是為大對(duì)象占個(gè)位置知纷,當(dāng)時(shí)間到了轉(zhuǎn)發(fā)消息給大對(duì)象。當(dāng)代理forwardInvocation: 方法首先接收一條消息傳遞給另一個(gè)對(duì)象陵霉,它將確保對(duì)象存在琅轧,如果不存在則創(chuàng)建。所有大對(duì)象的消息都是通過(guò)代理踊挠,因此乍桂,對(duì)于其余程序而言,代理和大對(duì)象是一樣的效床。

轉(zhuǎn)發(fā)個(gè)繼承

盡管轉(zhuǎn)發(fā)模仿繼承睹酌,NSObject類(lèi)不會(huì)混淆兩者。像respondsToSelector: 方法和isKindOfClass: 方法只查看繼承層次結(jié)構(gòu)剩檀,不在轉(zhuǎn)發(fā)鏈上憋沿。
如果使用轉(zhuǎn)發(fā)來(lái)設(shè)置代理對(duì)象或擴(kuò)展一個(gè)類(lèi)的功能,轉(zhuǎn)發(fā)機(jī)制必須如同繼承一樣透明谨朝。如果想讓你的對(duì)象假裝它們真正繼承它們轉(zhuǎn)發(fā)消息的對(duì)象的行為卤妒,需要重新實(shí)現(xiàn)respondsToSelector: 方法和isKindOfClass: 方法,包括轉(zhuǎn)發(fā)算法字币。

- (BOOL)respondsToSelector:(SEL)aSelector  
{  
    if ( [super respondsToSelector:aSelector] )  
        return YES;  
    else {  
        /* Here, test whether the aSelector message can     * 
         * be forwarded to another object and whether that  * 
         * object can respond to it. Return YES if it can.  */  
    }  
    return NO;  
}  

除了respondsToSelector: 方法和isKindOfClass: 方法则披,instancesRespondToSelector:方法也要反應(yīng)轉(zhuǎn)發(fā)算法。如果使用協(xié)議洗出,conformsToProtocol:方法需要添加到列表中士复。同樣的,如果一個(gè)對(duì)象轉(zhuǎn)發(fā)它接收到的任何遠(yuǎn)程消息翩活,它必須有的一個(gè)methodSignatureForSelector: 版本阱洪,該版本必須可以準(zhǔn)確返回描述方法。例如菠镇,如果一個(gè)對(duì)象可以轉(zhuǎn)發(fā)消息給它的代理冗荸,需要實(shí)現(xiàn)如下methodSignatureForSelector: 方法:

- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector  
{  
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];  
    if (!signature) {  
       signature = [surrogate methodSignatureForSelector:selector];  
    }  
    return signature;  
}  

注意:這是一種先進(jìn)技術(shù),只有在沒(méi)有其他解決方案可行的情況下才適用利耍。它并不打算取代繼承蚌本。如果你必須使用這種技術(shù)盔粹,確保你完全理解轉(zhuǎn)發(fā)類(lèi)和你要轉(zhuǎn)發(fā)的類(lèi)的行為。

類(lèi)型編碼

為了協(xié)助運(yùn)行時(shí)系統(tǒng)程癌,編譯器用字符串為每個(gè)方法的返回值和參數(shù)類(lèi)型和方法選擇器編碼舷嗡。使用的編碼方案在其他情況下也很有用,所以它是public 的嵌莉,可用于@encode() 編譯器指令进萄。當(dāng)給定一個(gè)類(lèi)型參數(shù),返回一個(gè)編碼類(lèi)型字符串锐峭。類(lèi)型可以是一個(gè)基本類(lèi)型如int中鼠,指針,結(jié)構(gòu)或聯(lián)合標(biāo)記只祠,或任何類(lèi)型的類(lèi)名兜蠕,事實(shí)上,都可以作為C sizeof() 運(yùn)算符的參數(shù)抛寝。

具體參見(jiàn)文檔

--

聲明屬性

當(dāng)編譯器遇到屬性聲明時(shí)(參見(jiàn)The Objective-C 編程語(yǔ)言中的聲明屬性),它生成與封閉類(lèi)曙旭、分類(lèi)或協(xié)議相關(guān)的描述性元數(shù)據(jù)盗舰。可以通過(guò)函數(shù)訪問(wèn)元數(shù)據(jù)桂躏,該函數(shù)支持通過(guò)類(lèi)或協(xié)議名稱(chēng)查找屬性钻趋,獲取屬性的類(lèi)型作為@encode 字符串,并復(fù)制property的屬性列表作為C字符串?dāng)?shù)組剂习。聲明的屬性列表可用于每個(gè)類(lèi)和協(xié)議蛮位。
屬性類(lèi)型和函數(shù)
屬性結(jié)構(gòu)為屬性描述符定義了一個(gè)不透明句柄。

typedef struct objc_property *Property;  

可以使用class_copyPropertyList 和 protocol_copyPropertyList 函數(shù)分別檢索屬性數(shù)組和類(lèi)(包括加載的類(lèi)別)和協(xié)議:

objc_property_t *class_copyPropertyList(Class cls, unsigned intint *outCount)  
objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned intint *outCount)  

例如鳞绕,類(lèi)聲明如下:

@interface Lender : NSObject {  
    float alone;  
}  
@property float alone;  
@end  

使用如下代碼失仁,可以得到屬性列表:

id LenderClass = objc_getClass("Lender");  
unsigned int outCount;  
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);  

可以使用property_getName函數(shù)來(lái)找到屬性名稱(chēng):

const charchar *property_getName(objc_property_t property)  

可以使用class_getProperty和protocol_getProperty函數(shù)分別獲取類(lèi)和協(xié)議中給定名稱(chēng)的屬性的引用:

objc_property_t class_getProperty(Class cls, const charchar *name)  
objc_property_t protocol_getProperty(Protocol *proto, const charchar *name, BOOL isRequiredProperty, BOOL isInstanceProperty)  

可以使用property_getAttributes 函數(shù)獲取屬性的名稱(chēng)和@encode類(lèi)型字符串。編碼類(lèi)型字符串詳情可查看類(lèi)型編碼们何;字符串詳情可查看屬性類(lèi)型字符串和property屬性描述例子萄焦。

const charchar *property_getAttributes(objc_property_t property)  

把這些放在一起,使用如下代碼冤竹,可以打印所有與類(lèi)關(guān)聯(lián)的屬性列表:

id LenderClass = objc_getClass("Lender");  
unsigned int outCount, i;  
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);  
for (i = 0; i < outCount; i++) {  
    objc_property_t property = properties[i];  
    fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));  
}  

參見(jiàn)文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拂封,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鹦蠕,更是在濱河造成了極大的恐慌冒签,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钟病,死亡現(xiàn)場(chǎng)離奇詭異萧恕,居然都是意外死亡霜定,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)廊鸥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)望浩,“玉大人,你說(shuō)我怎么就攤上這事惰说∧サ拢” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵吆视,是天一觀的道長(zhǎng)典挑。 經(jīng)常有香客問(wèn)我,道長(zhǎng)啦吧,這世上最難降的妖魔是什么您觉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮授滓,結(jié)果婚禮上琳水,老公的妹妹穿的比我還像新娘。我一直安慰自己般堆,他們只是感情好在孝,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著淮摔,像睡著了一般私沮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上和橙,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天仔燕,我揣著相機(jī)與錄音,去河邊找鬼魔招。 笑死晰搀,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的仆百。 我是一名探鬼主播厕隧,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼俄周!你這毒婦竟也來(lái)了吁讨?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤峦朗,失蹤者是張志新(化名)和其女友劉穎建丧,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體波势,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡翎朱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年橄维,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拴曲。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡争舞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出澈灼,到底是詐尸還是另有隱情竞川,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布叁熔,位于F島的核電站委乌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏荣回。R本人自食惡果不足惜遭贸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望心软。 院中可真熱鬧壕吹,春花似錦、人聲如沸糯累。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)泳姐。三九已至,卻和暖如春暂吉,著一層夾襖步出監(jiān)牢的瞬間胖秒,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工慕的, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留阎肝,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓肮街,卻偏偏與公主長(zhǎng)得像风题,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嫉父,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容