天天看點

OC--消息機制

消息機制深入了解:

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