cocoa 訊息機制

2021-07-12 04:17:07 字數 3927 閱讀 5429

在oc中訊息的呼叫都是動態的(如呼叫方法),背後有著編譯器的支援,要搞清楚背後的機制需要先弄懂class,sel,imp這三個概念。他們的定義如下:

[objc]view plain

copy

print?

typedef

struct

objc_class *class;  

typedef

struct objc_object  *id;  

typedef

struct objc_selector   *sel;     

typedef

id (*imp)(id, sel, ...);  

class的定義:

[objc]view plain

copy

print?

struct objc_class ;  

由此可見,class 是指向類結構體的指標,該類結構體含有乙個指向其父類類結構的指標,該類方法的鍊錶,該類方法的快取以及其他必要資訊。

nsobject 的class 方法就返回這樣乙個指向其類結構的指標。每乙個類例項物件的第乙個例項變數是乙個指向該物件的類結構的指標,叫做isa。通過該指標,物件可以訪問它對應的類以及相應的父類。如圖一所示:

如圖一所示,圓形所代表的例項物件的第乙個例項變數為 isa,它指向該類的類結構 the object』s class。而該類結構有乙個指向其父類類結構的指標superclass, 以及自身訊息名稱(selector)/實現位址(address)的方法鍊錶。

方法的含義:

注意這裡所說的方法鍊錶裡面儲存的是method 型別的。圖一中selector 就是指 method的 sel,  address就是指method的 imp。 method 在標頭檔案 objc_class.h中定義如下:

typedef struct objc_method *method;

typedef struct objc_ method   

}  if

(!meth) meth = _class_getmethod(cls, sel);  

if(!meth  &&  withresolver) meth = _class_resolvemethod(cls, sel);  

return

meth;  

}  

通過分析上面的**,可以看到,查詢時:

1,首先去該類的方法cache中查詢,如果找到了就返回它;

2,如果沒有找到,就去該類的方法列表中查詢。如果在該類的方法列表中找到了,則將imp返回,並將它加入cache中快取起來。根據最近使用原則,這個方法再次呼叫的可能性很大,快取起來可以節省下次呼叫再次查詢的開銷。3,3,如果在該類的方法列表中沒找到對應的imp,在通過該類結構中的super_class指標在其父類結構的方法列表中去查詢,直到在某個父類的方法列表中找到對應的imp,返回它,並加入cache中;

4,如果在自身以及所有父類的方法列表中都沒有找到對應的imp,則看是不是可以進行動態方法決議(後面有專文講述這個話題);

5,如果動態方法決議沒能解決問題,進入下面要講的訊息**流程。

便利函式:

我們可以通過nsobject的一些方法獲取執行時資訊或動態執行一些訊息:

class   返回物件的類;

iskindofclass 和 ismemberofclass檢查物件是否在指定的類繼承體系中;

respondstoselector 檢查物件能否相應指定的訊息;

conformstoprotocol 檢查物件是否實現了指定協議類的方法;

methodforselector  返回指定方法實現的位址。

performselector:withobject 執行sel 所指代的方法。

訊息**:

通常,給乙個物件傳送它不能處理的訊息會得到出錯提示,然而,objective-c執行時系統在丟擲錯誤之前,會給訊息接收物件傳送一條特別的訊息forwardinvocation 來通知該物件,該訊息的唯一引數是個nsinvocation型別的物件——該物件封裝了原始的訊息和訊息的引數。

我們可以實現forwardinvocation:方法來對不能處理的訊息做一些預設的處理,也可以將訊息**給其他物件來處理,而不丟擲錯誤。

關於訊息**的作用,可以考慮如下情景:假設,我們需要設計乙個能夠響應negotiate訊息的物件,並且能夠包括其它型別的物件對訊息的響應。 通過在negotiate方法的實現中將negotiate訊息**給其它的物件來很容易的達到這一目的。

更進一步,假設我們希望我們的物件和另外乙個類的物件對negotiate的訊息的響應完全一致。一種可能的方式就是讓我們的類繼承其它類的方法實現。 然後,有時候這種方式不可行,因為我們的類和其它類可能需要在不同的繼承體系中響應negotiate訊息。

雖然我們的類無法繼承其它類的negotiate方法,但我們仍然可以提供乙個方法實現,這個方法實現只是簡單的將negotiate訊息**給其他類的物件,就好像從其它類那兒「借」來的現一樣。如下所示:

- negotiate

if ([someotherobject respondstoselector:@selector(negotiate)])

return [someotherobject negotiate];

return self;

這種方式顯得有欠靈活,特別是有很多訊息都希望傳遞給其它物件時,我們就必須為每一種訊息提供方法實現。此外,這種方式不能處理未知的訊息。當我們寫下**時,所有我們需要**的訊息的集合都必須確定。然而,實際上,這個集合會隨著執行時事件的發生,新方法或者新類的定義而變化。

forwardinvocation:訊息給這個問題提供了乙個更特別的,動態的解決方案:當乙個物件由於沒有相應的方法實現而無法響應某訊息時,執行時系統將通過forwardinvocation:訊息通知該物件。每個物件都從nsobject類中繼承了forwardinvocation:方法。然而,nsobject中的方法實現只是簡單地呼叫了doesnotrecognizeselector:。通過實現我們自己的forwardinvocation:方法,我們可以在該方法實現中將訊息**給其它物件。

要**訊息給其它物件,forwardinvocation:方法所必須做的有:

1,決定將訊息**給誰,並且

2,將訊息和原來的引數一塊**出去。

訊息可以通過invokewithtarget:方法來**:

- (void) forwardinvocation:(nsinvocation *)aninvocation

if ([someotherobject respondstoselector:[aninvocation selector]])

[aninvocation invokewithtarget:someotherobject];

else

[super forwardinvocation:aninvocation];

**訊息後的返回值將返回給原來的訊息傳送者。您可以將返回任何型別的返回值,包括: id,結構體,浮點數等。

forwardinvocation:方法就像乙個不能識別的訊息的分發中心,將這些訊息**給不同接收物件。或者它也可以象乙個運輸站將所有的訊息都傳送給同乙個接收物件。它可以將乙個訊息翻譯成另外乙個訊息,或者簡單的"吃掉「某些訊息,因此沒有響應也沒有錯誤。forwardinvocation:方法也可以對不同的訊息提供同樣的響應,這一切都取決於方法的具體實現。該方法所提供是將不同的物件鏈結到訊息鏈的能力。

注意:forwardinvocation:方法只有在訊息接收物件中無法正常響應訊息時才會被呼叫。 所以,如果我們希望乙個物件將 negotiate 訊息**給其它物件,則這個物件不能有negotiate 方法,也不能在動態方法決議過程中為之提供實現。否則,forwardinvocation:將不可能會被呼叫。

windows訊息機制

一 windows中有乙個系統訊息佇列,對於每乙個正在執行的windows應用程式,系統為其建立乙個 訊息佇列 即應用程式佇列,用來存放該程式可能 建立的各種視窗的訊息。應用程式中含有一段稱作 訊息迴圈 的 用來從訊息佇列中檢索這些訊息並把它們分發到相應的視窗函式中。二 windows為當前執行的每...

訊息機制 WSAAsyncSelect

訊息機制 wsaasyncselect wsaasyncselect基於windows訊息機制非同步i o模型,為特定網路事件指定系統通知資訊.函式定義 int wsaasyncselect socket s,hwnd hwnd,unsigned int wmsg,long levent 1 引數4...

FD WRITE 訊息機制

我本想把傳送和接收分開作為兩部分,但是最後我決定只略微解釋一下 fd read 留下更多的時間來說明更複雜的 fd write fd read 事件非常容易掌握.當有資料傳送過來時,winsock 會以 fd read 事件通知你,對於每乙個 fd read 事件,你需要像下面這樣呼叫 recv i...