消息機制深入了解:
- Objective-C是一門動态語言,其動态特性很大程度上是歸因于其消息機制。
- Objective-C采用了消息發送、消息選擇器、動态類型和運作時檢查等機制。
- 消息選擇器
-
- 方法和選擇器:
-
- 在OC中,selector有兩重含義:一是在源代碼中它代表了向對象發送消息的方法名稱;二是在編譯後它代表了方法名稱被編譯過後一個統一的辨別。
- 編譯後的方法名稱有一個類型名稱為SEL,所有的同名方法都共享同一個selector。
- 一定要注意:方法名稱和方法不是一個東西。方法是method,類型是IMP,類似C語言的函數,是指對象的一段可運作的代碼。 而方法名稱是selector,類型是SEL,是給方法取得名字,不同的類的同名方法共享一個方法名稱的編号。
- 采用選擇器展現了OC的多态性和動态綁定性。
- 要使用動态綁定,同名的方法必須采用同樣的參數類型和傳回類型。
- _cmd是什麼? _cmd表示目前方法的selector,可以指派給SEL類型的變量,可以作為參數傳遞。
- 運作時改變消息:在NSObject協定中定義了performSelector:方法,它起到的作用和直接調用方法是一樣的,但是由于performSelector:的參數是可以替換的,是以容許在運作時改變消息。
- Target-Action模式:
-
- target是一個指向其它對象的指針,action是會發給target的消息。例如:[button addTarget:anObject action:angel forControlEvents:UIControlEventTouchUpInside]; // 這條語句給button對象添加了一個target,是對象anObject,并确定了待發送消息的是angel,相應的動作是當使用者觸摸按鈕之後擡起手指時。 當使用者觸摸按鈕并擡起手指後,button按鈕會發送消息給anOjbject,實際上button内部會調用方法:[button performSelector:angel withOjbect:anObject]; // 所有的動作消息隻有一個參數,就是消息的發送者,類型為id。
- 擷取方法的位址:
-
- OC專門定義了一種類型IMP,可以通過IMP來擷取方法位址:例如:IMP = impStudy = [student methodForSelector:@selector(study)];
- 其實IMP是一個函數指針,他的定義式:typedef id (*IMP) (id, SEL, …);
- 這個函數有兩個以上的參數,第一個是運作該函數的對象,第二個是對象的方法名,後面是這個方法所帶的參數。我們可以如下調用:impStudy(student, @selector(study)); // 這個語句和[student study];的作用是一樣的,差別在于使用函數指針是直接調用方法的位址,更快一點。而使用[student study];要先給student對象發送study消息,然後運作系統會去尋找消息對應的名稱,再找到方法的位址,然後去運作,效率會低一點。
- 容許靜态行為:OC是一門動态語言,有很大的靈活性和優點,但是也會帶來一些不好的地方,有時候我們需要暫時屏蔽其動态特性。
-
- 預設的動态行為:
-
- 對象的記憶體空間是動态申請的,由運作時刻的類方法來建立對象執行個體。
- 對象都是動态類型的。在編譯時期的源代碼中,對象可以是id類型的,知道運作時刻才确定具體的類(進而确定其方法和資料結構)。
- 消息和方法也是動态綁定的。運作時系統會将消息中得消息選擇器與消息接受者的方法進行比對。
- 靜态類型
-
- 在定義變量的時候如果我們使用id,而使用具體的某一類的名稱,我們稱為靜态類型:例如:id person; // 動态類型 Student *stu; // 靜态類型
- 靜态類型給了編譯器有關對象更多得資訊,相比單純的采用id類型,有以下不同點:1.容許編譯期間進行類型檢查 2.使代碼更具可讀性 3.靜态類型的對象不用遵循“同名的方法必須具備同樣的參數和傳回值”的規則 4.可以通過結構體指針直接通路對象的執行個體變量
- isMemberOfClass: //是否是給定類的執行個體
- isKingOfClass: //
- 運作時消息檢查:
-
- 當對象接收到一條它無法實作的消息的時候,會産生錯誤。因為消息是在運作時刻才發出,是以出錯的時候不一定那麼明顯。這時候我們可以用respondsToSelector:方法來檢測 一個對象時候能響應某條消息。
- 例子:Student *stu = [[Student alloc] init];if ([stu respondsToSelector:@selector(englishTest)]) [stu englistTest];else fprintf(stderr, "%s can't, test \n", [NSStringFromClass([stu class]) UTF8String]);
- 消息傳遞:
-
- 有兩個對象A、B,當給A發送一條消息,但是A無法處理的時候它會不會傳遞給B對象呢? 答案是肯定的。
- 在添加消息轉發功能前,必須要覆寫兩個方法:methodSignatureForSelector: // 為另一個類實作消息建立一個有效的方法簽名forwardInvocation: // 将選擇器轉發給一個真正實作了該消息的對象