青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

羅朝輝(飄飄白云)

關注嵌入式操作系統,移動平臺,圖形開發。-->加微博 ^_^

  C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
  85 隨筆 :: 0 文章 :: 169 評論 :: 0 Trackbacks

深入淺出ObjC消息   

羅朝輝 (http://ww.cppblog.com/kesalin)

轉載請注明出處

在入門級別的ObjC 教程中,我們常對從C++或Java 或其他面向對象語言轉過來的程序員說,ObjC 中的方法調用(ObjC中的術語為消息)跟其他語言中的方法調用差不多,只是形式有些不同而已。

譬如C++ 中的:

Bird * aBird = new Bird();

aBird->fly();

在ObjC 中則如下:

Bird * aBird = [[Bird alloc] init];

[aBird fly];


乍看起來,好像只是書寫形式不同而已,實則差異大矣。C++中的方法調用可能是動態的,也可能是靜態的;而ObjC中的消息都為動態的。下文將詳細介紹為什么是動態的,以及編譯器在這背后做了些什么事情。


要說清楚消息這個話題,我們必須先來了解三個概念Class, SEL, IMP,它們在objc/objc.h 中定義:

typedef struct objc_class *Class;

typedef struct objc_object {

    Class isa;

} *id;

 

typedef struct objc_selector   *SEL;   

typedef id (*IMP)(id, SEL, ...);

 

Class 的含義

Class 被定義為一個指向 objc_class的結構體指針,這個結構體表示每一個類的類結構。而 objc_class 在objc/objc_class.h中定義如下:

struct objc_class {

    struct objc_class super_class;  /*父類*/

    const char *name;                         /*類名字*/

    long version;                                  /*版本信息*/

    long info;                                        /*類信息*/

    long instance_size;                      /*實例大小*/

    struct objc_ivar_list *ivars;          /*實例參數鏈表*/

    struct objc_method_list **methodLists;  /*方法鏈表*/

    struct objc_cache *cache;                    /*方法緩存*/

    struct objc_protocol_list *protocols;   /*協議鏈表*/

};

 

由此可見,Class 是指向類結構體的指針,該類結構體含有一個指向其父類類結構的指針,該類方法的鏈表,該類方法的緩存以及其他必要信息。

NSObject 的class 方法就返回這樣一個指向其類結構的指針。每一個類實例對象的第一個實例變量是一個指向該對象的類結構的指針,叫做isa。通過該指針,對象可以訪問它對應的類以及相應的父類。如圖一所示:


 

如圖一所示,圓形所代表的實例對象的第一個實例變量為 isa,它指向該類的類結構 The object’s class。而該類結構有一個指向其父類類結構的指針superclass, 以及自身消息名稱(selector)/實現地址(address)的方法鏈表。

 

方法的含義:

注意這里所說的方法鏈表里面存儲的是Method 類型的。圖一中selector 就是指 Method的 SEL,  address就是指Method的 IMP。

 

一個方法 Method,其包含一個方法選標 SEL – 表示該方法的名稱,一個types – 表示該方法參數的類型,一個 IMP  - 指向該方法的具體實現的函數指針。

Method 在頭文件 objc_class.h中定義如下:

typedef struct objc_method *Method;

typedef struct objc_ method {

    SEL method_name;

    char *method_types;

    IMP method_imp;

};


SEL 的含義:

在前面我們看到方法選標 SEL 的定義為:

typedef struct objc_selector   *SEL;   

它是一個指向 objc_selector 指針,表示方法的名字/簽名。如下所示,打印出 selector。

-(NSInteger)maxIn:(NSInteger)a theOther:(NSInteger)b

{

    return (a > b) ? a : b;

}

 

NSLog(@"SEL=%s", @selector(maxIn:theOther:));

 

輸出:SEL=maxIn:theOther:

 

不同的類可以擁有相同的 selector,這個沒有問題,因為不同類的實例對象performSelector相同的 selector 時,會在各自的消息選標(selector)/實現地址(address) 方法鏈表中根據 selector 去查找具體的方法實現IMP, 然后用這個方法實現去執行具體的實現代碼。這是一個動態綁定的過程,在編譯的時候,我們不知道最終會執行哪一些代碼,只有在執行的時候,通過selector去查詢,我們才能確定具體的執行代碼。

IMP 的含義:

在前面我們也看到 IMP 的定義為:

typedef id (*IMP)(id, SEL, ...);

根據前面id 的定義,我們知道 id是一個指向 objc_object 結構體的指針,該結構體只有一個成員isa,所以任何繼承自 NSObject 的類對象都可以用id 來指代,因為 NSObject 的第一個成員實例就是isa。

至此,我們就很清楚地知道 IMP  的含義:IMP 是一個函數指針,這個被指向的函數包含一個接收消息的對象id(self  指針), 調用方法的選標 SEL (方法名),以及不定個數的方法參數,并返回一個id。也就是說 IMP 是消息最終調用的執行代碼,是方法真正的實現代碼 。我們可以像在C語言里面一樣使用這個函數指針。

NSObject 類中的methodForSelector:方法就是這樣一個獲取指向方法實現IMP 的指針,methodForSelector:返回的指針和賦值的變量類型必須完全一致,包括方法的參數類型和返回值類型。

下面的例子展示了怎么使用指針來調用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);

 

 

使用methodForSelector:來避免動態綁定將減少大部分消息的開銷,但是這只有在指定的消息被重復發送很多次時才有意義,例如上面的for循環。

注意,methodForSelector:是Cocoa運行時系統的提供的功能,而不是Objective-C語言本身的功能。

 

消息調用過程:

至此我們對ObjC 中的消息應該有個大致思路了:示例

Bird * aBird = [[Bird alloc] init];

[aBird fly];

中對 fly 的調用,編譯器通過插入一些代碼,將之轉換為對方法具體實現IMP的調用,這個 IMP是通過在 Bird 的類結構中的方法鏈表中查找名稱為fly 的 選標SEL 對應的具體方法實現找到的。

上面的思路還有一些沒有提及的話題,比如說編譯器插入了什么代碼,如果在方法鏈表中沒有找到對應的 IMP又會如何,這些話題在下面展開。

 

消息函數 obj_msgSend:

編譯器會將消息轉換為對消息函數 objc_msgSend的調用,該函數有兩個主要的參數:消息接收者id 和消息對應的方法選標 SEL, 同時接收消息中的任意參數:

id objc_msgSend(id theReceiver, SELtheSelector, ...)

如上面的消息 [aBird fly]會被轉換為如下形式的函數調用:

objc_msgSend(aBird, @selector(fly));

 

該消息函數做了動態綁定所需要的一切工作:
1,它首先找到 SEL 對應的方法實現 IMP。因為不同的類對同一方法可能會有不同的實現,所以找到的方法實現依賴于消息接收者的類型。
2, 然后將消息接收者對象(指向消息接收者對象的指針)以及方法中指定的參數傳遞給方法實現 IMP。
3, 最后,將方法實現的返回值作為該函數的返回值返回。

編譯器會自動插入調用該消息函數objc_msgSend的代碼,我們無須在代碼中顯示調用該消息函數。當objc_msgSend找到方法對應的實現時,它將直接調用該方法實現,并將消息中所有的參數都傳遞給方法實現,同時,它還將傳遞兩個隱藏的參數:消息的接收者以及方法名稱 SEL。這些參數幫助方法實現獲得了消息表達式的信息。它們被認為是”隱藏“的是因為它們并沒有在定義方法的源代碼中聲明,而是在代碼編譯時是插入方法的實現中的。

盡管這些參數沒有被顯示聲明,但在源代碼中仍然可以引用它們(就象可以引用消息接收者對象的實例變量一樣)。在方法中可以通過self來引用消息接收者對象,通過選標_cmd來引用方法本身。在下面的例子中,_cmd 指的是strange方法,self指的收到strange消息的對象。

- strange

{

    id target = getTheReceiver();

    SEL method = getTheMethod();

 

    if (target == self || mothod == _cmd)

        return nil;

 

    return [target performSelector:method];

}

 

在這兩個參數中,self更有用一些。實際上,它是在方法實現中訪問消息接收者對象的實例變量的途徑。

 

查找 IMP 的過程:

前面說了,objc_msgSend 會根據方法選標 SEL 在類結構的方法列表中查找方法實現IMP。這里頭有一些文章,我們在前面的類結構中也看到有一個叫objc_cache *cache 的成員,這個緩存為提高效率而存在的。每個類都有一個獨立的緩存,同時包括繼承的方法和在該類中定義的方法。。

 下面來剖析一段蘋果官方的源碼:

static Method look_up_method(Class cls, SEL sel, BOOL withCache, BOOL withResolver)
{
    Method meth 
= NULL;

    
if (withCache) {
        meth 
= _cache_getMethod(cls, sel, &_objc_msgForward_internal);
        
if (meth == (Method)1) {
            
// Cache contains forward:: . Stop searching.
            return NULL;
        }
    }

    
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:將不可能會被調用。

參考資料:

Objective-CRuntime Reference:

http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html

Objective-C Runtime Programming Guide:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html


posted on 2011-08-15 20:20 羅朝輝 閱讀(7991) 評論(1)  編輯 收藏 引用 所屬分類: 移動開發Cocoa 開發

評論

# re: 深入淺出ObjC之消息 [未登錄] 2011-08-15 22:54 chentan
非常好的文章, 燃起了我對objc學習的興趣,謝謝  回復  更多評論
  

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲毛片播放| 亚洲欧美日韩一区在线| 亚洲免费成人av| 久久性天堂网| 久久夜色精品| 亚洲精品中文字幕有码专区| 亚洲电影免费观看高清完整版在线| 国产精品高清一区二区三区| 亚洲第一网站免费视频| 国产精品yjizz| 久久大香伊蕉在人线观看热2| 久久福利毛片| 亚洲国产日韩一区| 欧美天天综合网| 欧美一区二区三区男人的天堂 | 国产精品亚洲网站| 美女啪啪无遮挡免费久久网站| 亚洲人成啪啪网站| 久久亚洲一区二区| 免播放器亚洲一区| 欧美视频中文字幕| 永久免费视频成人| 国产麻豆午夜三级精品| 欧美大学生性色视频| 亚洲影院在线| 最新国产乱人伦偷精品免费网站| 欧美亚洲一区二区在线| 亚洲精品美女免费| 亚洲国产精品传媒在线观看| 国产一在线精品一区在线观看| 欧美另类综合| 欧美片在线播放| 国外成人在线| 在线成人av| 午夜精品视频| 欧美中在线观看| 亚洲日本黄色| 久久久久久综合网天天| 欧美一区二区在线播放| 欧美喷潮久久久xxxxx| 激情一区二区三区| 亚洲韩国日本中文字幕| 小黄鸭精品密入口导航| 亚洲国产欧美一区二区三区久久| 美日韩免费视频| 久久久久看片| 久久久久九九九| 久久久久欧美| 一区二区精品国产| 一区二区三区视频免费在线观看| 久久天堂成人| 国产曰批免费观看久久久| 亚洲免费综合| 中文在线资源观看视频网站免费不卡| 一本色道久久加勒比精品| 欧美99久久| 欧美三级日本三级少妇99| 亚洲欧美亚洲| 久久精品亚洲一区| 久久久久久久久岛国免费| 一区二区三区免费观看| 欧美精品免费在线| 国产精品日韩一区二区| 亚洲一区二区三区高清不卡| 篠田优中文在线播放第一区| 99国产精品自拍| 亚洲欧美日韩第一区| 欧美日韩中字| 亚洲欧美精品| 欧美亚洲色图校园春色| 韩国三级在线一区| 免费亚洲一区| 欧美连裤袜在线视频| 亚洲视频一区在线| 久久一区二区三区超碰国产精品| 性欧美暴力猛交另类hd| 欧美激情精品久久久久久变态| 欧美午夜久久| 久久本道综合色狠狠五月| 欧美中文字幕第一页| 欧美日韩亚洲一区二| 国产一区二区欧美日韩| 亚洲伦理中文字幕| 亚洲毛片在线看| 国产精品自在在线| 卡一卡二国产精品| 亚洲欧美日韩国产中文在线| 国产欧美精品va在线观看| 亚洲片区在线| 久久综合伊人77777麻豆| 久久久久久久精| 一本一道久久综合狠狠老精东影业| 久久久久久999| 欧美激情2020午夜免费观看| 亚洲欧美国产高清va在线播| 久久av在线| 亚洲一区二区三| 久久久噜久噜久久综合| 一本色道**综合亚洲精品蜜桃冫 | 欧美一区二区视频观看视频| 亚洲大片在线观看| 在线亚洲自拍| 亚洲国产日韩欧美在线99 | 国产精品99久久久久久人 | 亚洲欧洲三级电影| 香蕉国产精品偷在线观看不卡| 亚洲激情啪啪| 欧美在线观看一区二区| 一区二区动漫| 亚洲人被黑人高潮完整版| 国产精品麻豆成人av电影艾秋| 国内精品视频在线观看| 欧美一区二区三区视频| 亚洲精品久久久久久久久| 国产精品分类| 亚洲国产一区二区三区在线播| 国产精品99一区| 欧美成人在线免费视频| 久久成人一区二区| 亚洲一区二区综合| 亚洲图片你懂的| 欧美日韩一区二区三区| 久热精品在线视频| 国产午夜一区二区三区| 久久久精品国产99久久精品芒果| 欧美国产日韩一区二区三区| 久久综合狠狠综合久久综合88| 国产精品地址| 夜夜嗨av一区二区三区中文字幕| 亚洲精品欧美日韩| 欧美成人精品影院| 亚洲电影视频在线| 裸体女人亚洲精品一区| 久久久久久久久蜜桃| 国产欧美日韩亚洲一区二区三区| 99ri日韩精品视频| 亚洲一区尤物| 国产精品嫩草99a| 亚洲专区在线| 激情欧美一区二区| 欧美在线网站| 麻豆精品在线播放| 91久久久久| 欧美精品久久99久久在免费线| 欧美激情免费观看| 国产精品永久入口久久久| 亚洲午夜激情免费视频| 久久国产精品99精品国产| 狠狠色丁香久久综合频道| 久久久久久久久岛国免费| 欧美高清视频免费观看| 亚洲精品在线视频观看| 欧美日韩视频在线一区二区| 亚洲一区二区三区乱码aⅴ蜜桃女| 欧美一区二区三区啪啪| 一区三区视频| 欧美视频在线观看免费| 亚洲伊人久久综合| 美女被久久久| av不卡免费看| 国产欧美日韩91| 久久综合狠狠综合久久综合88| 亚洲欧美日韩国产精品| 国产人妖伪娘一区91| 久久久久国色av免费观看性色| 亚洲经典三级| 欧美一区二区三区日韩视频| 精品91在线| 欧美日韩视频| 久久精品欧美日韩精品| 久久成人亚洲| 亚洲精品日日夜夜| 国产日韩欧美亚洲| 欧美日韩成人在线视频| 亚洲国产精品久久久久秋霞影院| 一区二区日本视频| 国内精品久久久久影院 日本资源| 久久一区二区三区四区| 99精品视频一区二区三区| 久久午夜电影| 一区二区三区免费在线观看| 久久中文久久字幕| 亚洲一区二区av电影| 亚洲成人自拍视频| 国产日韩视频| 欧美视频四区| 免费视频久久| 欧美伊人久久久久久久久影院| 亚洲精品综合久久中文字幕| 欧美一区影院| 亚洲伊人观看| 一区二区三区日韩在线观看| 国语自产精品视频在线看8查询8| 欧美日韩999| 欧美激情一区二区三区四区| 久久人人超碰| 久久精品30| 欧美一区二区三区喷汁尤物| 一区电影在线观看| 亚洲精品国产精品国自产观看浪潮|