久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Objective C Runtime

 quasiceo 2015-03-05

Objective-C Runtime

on objc, runtime, messaging

Objective-C

Objective-C 擴展了 C 語言,,并加入了面向?qū)ο筇匦院?Smalltalk 式的消息傳遞機制,。而這個擴展的核心是一個用 C 和 編譯語言 寫的 Runtime 庫。它是 Objective-C 面向?qū)ο蠛蛣討B(tài)機制的基石,。

Objective-C 是一個動態(tài)語言,,這意味著它不僅需要一個編譯器,也需要一個運行時系統(tǒng)來動態(tài)得創(chuàng)建類和對象,、進行消息傳遞和轉(zhuǎn)發(fā),。理解 Objective-C 的 Runtime 機制可以幫我們更好的了解這個語言,適當?shù)臅r候還能對語言進行擴展,,從系統(tǒng)層面解決項目中的一些設計或技術(shù)問題,。了解 Runtime ,要先了解它的核心 - 消息傳遞 (Messaging),。

消息傳遞(Messaging)

I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is “messaging” – that is what the kernal[sic] of Smalltalk is all about... The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviors should be.

Alan Kay 曾多次強調(diào) Smalltalk 的核心不是面向?qū)ο?,面向?qū)ο笾皇?the lesser ideas,消息傳遞才是 the big idea,。

在很多語言,,比如 C ,調(diào)用一個方法其實就是跳到內(nèi)存中的某一點并開始執(zhí)行一段代碼,。沒有任何動態(tài)的特性,,因為這在編譯時就決定好了。而在 Objective-C 中,,[object foo] 語法并不會立即執(zhí)行 foo 這個方法的代碼,。它是在運行時給 object 發(fā)送一條叫 foo 的消息。這個消息,,也許會由 object 來處理,,也許會被轉(zhuǎn)發(fā)給另一個對象,或者不予理睬假裝沒收到這個消息,。多條不同的消息也可以對應同一個方法實現(xiàn),。這些都是在程序運行的時候決定的,。

事實上,,在編譯時你寫的 Objective-C 函數(shù)調(diào)用的語法都會被翻譯成一個 C 的函數(shù)調(diào)用 - objc_msgSend() ,。比如,下面兩行代碼就是等價的:

[array insertObject:foo atIndex:5];

objc_msgSend(array, @selector(insertObject:atIndex:), foo, 5);

消息傳遞的關鍵藏于 objc_object 中的 isa 指針和 objc_class 中的 class dispatch table,。

objc_object, objc_class 以及 Ojbc_method

在 Objective-C 中,,類、對象和方法都是一個 C 的結(jié)構(gòu)體,,從 objc/objc.h 頭文件中,,我們可以找到他們的定義:

struct objc_object {  
    Class isa  OBJC_ISA_AVAILABILITY;
};

struct objc_class {  
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    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;
#endif
};

struct objc_method_list {  
    struct objc_method_list *obsolete;
    int method_count;

#ifdef __LP64__
    int space;
#endif

    /* variable length structure */
    struct objc_method method_list[1];
};

struct objc_method {  
    SEL method_name;
    char *method_types;    /* a string representing argument/return types */
    IMP method_imp;
};

objc_method_list 本質(zhì)是一個有 objc_method 元素的可變長度的數(shù)組。一個 objc_method 結(jié)構(gòu)體中有函數(shù)名,,也就是SEL,,有表示函數(shù)類型的字符串 (見 Type Encoding) ,以及函數(shù)的實現(xiàn)IMP,。

從這些定義中可以看出發(fā)送一條消息也就 objc_msgSend 做了什么事,。舉 objc_msgSend(obj, foo) 這個例子來說:

  1. 首先,通過 obj 的 isa 指針找到它的 class ;
  2. 在 class 的 method list 找 foo ;
  3. 如果 class 中沒到 foo,,繼續(xù)往它的 superclass 中找 ;
  4. 一旦找到 foo 這個函數(shù),,就去執(zhí)行它的實現(xiàn)IMP .

但這種實現(xiàn)有個問題,效率低,。但一個 class 往往只有 20% 的函數(shù)會被經(jīng)常調(diào)用,,可能占總調(diào)用次數(shù)的 80% 。每個消息都需要遍歷一次 objc_method_list 并不合理,。如果把經(jīng)常被調(diào)用的函數(shù)緩存下來,,那可以大大提高函數(shù)查詢的效率。這也就是 objc_class 中另一個重要成員 objc_cache 做的事情 - 再找到 foo 之后,,把 foo 的 method_name 作為 key ,,method_imp 作為 value 給存起來。當再次收到 foo 消息的時候,,可以直接在 cache 里找到,,避免去遍歷 objc_method_list.

動態(tài)方法解析和轉(zhuǎn)發(fā)

在上面的例子中,如果 foo 沒有找到會發(fā)生什么,?通常情況下,,程序會在運行時掛掉并拋出 unrecognized selector sent to … 的異常。但在異常拋出前,,Objective-C 的運行時會給你三次拯救程序的機會:

  1. Method resolution
  2. Fast forwarding
  3. Normal forwarding

Method Resolution

首先,,Objective-C 運行時會調(diào)用 +resolveInstanceMethod: 或者 +resolveClassMethod:,讓你有機會提供一個函數(shù)實現(xiàn),。如果你添加了函數(shù)并返回 YES,, 那運行時系統(tǒng)就會重新啟動一次消息發(fā)送的過程。還是以 foo 為例,,你可以這么實現(xiàn):

void fooMethod(id obj, SEL _cmd)  
{
    NSLog(@"Doing foo");
}

+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
    if(aSEL == @selector(foo:)){
        class_addMethod([self class], aSEL, (IMP)fooMethod, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod];
}

Core Data 有用到這個方法,。NSManagedObjects 中 properties 的 getter 和 setter 就是在運行時動態(tài)添加的,。

如果 resolve 方法返回 NO ,運行時就會移到下一步:消息轉(zhuǎn)發(fā)(Message Forwarding),。

PS:iOS 4.3 加入很多新的 runtime 方法,,主要都是以 imp 為前綴的方法,比如 imp_implementationWithBlock() 用 block 快速創(chuàng)建一個 imp ,。
上面的例子可以重寫成:

IMP fooIMP = imp_implementationWithBlock(^(id _self) {  
    NSLog(@"Doing foo");
});

class_addMethod([self class], aSEL, fooIMP, "v@:");  

Fast forwarding

如果目標對象實現(xiàn)了 -forwardingTargetForSelector: ,,Runtime 這時就會調(diào)用這個方法,給你把這個消息轉(zhuǎn)發(fā)給其他對象的機會,。

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if(aSelector == @selector(foo:)){
        return alternateObject;
    }
    return [super forwardingTargetForSelector:aSelector];
}

只要這個方法返回的不是 nil 和 self,,整個消息發(fā)送的過程就會被重啟,當然發(fā)送的對象會變成你返回的那個對象,。否則,,就會繼續(xù) Normal Fowarding 。

這里叫 Fast ,,只是為了區(qū)別下一步的轉(zhuǎn)發(fā)機制,。因為這一步不會創(chuàng)建任何新的對象,但下一步轉(zhuǎn)發(fā)會創(chuàng)建一個 NSInvocation 對象,,所以相對更快點,。

Normal forwarding

這一步是 Runtime 最后一次給你挽救的機會。首先它會發(fā)送 -methodSignatureForSelector: 消息獲得函數(shù)的參數(shù)和返回值類型,。如果 -methodSignatureForSelector: 返回 nil ,Runtime 則會發(fā)出 -doesNotRecognizeSelector: 消息,,程序這時也就掛掉了,。如果返回了一個函數(shù)簽名,Runtime 就會創(chuàng)建一個 NSInvocation 對象并發(fā)送 -forwardInvocation: 消息給目標對象,。

NSInvocation 實際上就是對一個消息的描述,,包括selector 以及參數(shù)等信息。所以你可以在 -forwardInvocation: 里修改傳進來的 NSInvocation 對象,,然后發(fā)送 -invokeWithTarget: 消息給它,,傳進去一個新的目標:

- (void)forwardInvocation:(NSInvocation *)invocation
{
    SEL sel = invocation.selector;

    if([alternateObject respondsToSelector:sel]) {
        [invocation invokeWithTarget:alternateObject];
    } 
    else {
        [self doesNotRecognizeSelector:sel];
    }
}

Cocoa 里很多地方都利用到了消息傳遞機制來對語言進行擴展,如 Proxies,、NSUndoManager 跟 Responder Chain,。NSProxy 就是專門用來作為代理轉(zhuǎn)發(fā)消息的;NSUndoManager 截取一個消息之后再發(fā)送,;而 Responder Chain 保證一個消息轉(zhuǎn)發(fā)給合適的響應者,。

總結(jié)

Objective-C 中給一個對象發(fā)送消息會經(jīng)過以下幾個步驟:

  1. 在對象類的 dispatch table 中嘗試找到該消息。如果找到了,跳到相應的函數(shù)IMP去執(zhí)行實現(xiàn)代碼,;
  2. 如果沒有找到,,Runtime 會發(fā)送 +resolveInstanceMethod: 或者 +resolveClassMethod: 嘗試去 resolve 這個消息;
  3. 如果 resolve 方法返回 NO,,Runtime 就發(fā)送 -forwardingTargetForSelector: 允許你把這個消息轉(zhuǎn)發(fā)給另一個對象,;
  4. 如果沒有新的目標對象返回,, Runtime 就會發(fā)送 -methodSignatureForSelector:-forwardInvocation: 消息,。你可以發(fā)送 -invokeWithTarget: 消息來手動轉(zhuǎn)發(fā)消息或者發(fā)送 -doesNotRecognizeSelector: 拋出異常。

利用 Objective-C 的 runtime 特性,,我們可以自己來對語言進行擴展,,解決項目開發(fā)中的一些設計和技術(shù)問題。下一篇文章,,我會介紹 Method Swizzling 技術(shù)以及如何利用 Method Swizzling 做 Logging,。

Reference

Message forwarding

Objective-c-messaging

The faster objc_msgSend

Understanding objective-c runtime

顧 鵬

Live with less, share with more. weibo: @no-computer

Shanghai http://

Share this post

Objective-C Runtime

on objc, runtime, messaging

Objective-C

Objective-C 擴展了 C 語言,并加入了面向?qū)ο筇匦院?Smalltalk 式的消息傳遞機制,。而這個擴展的核心是一個用 C 和 編譯語言 寫的 Runtime 庫,。它是 Objective-C 面向?qū)ο蠛蛣討B(tài)機制的基石。

Objective-C 是一個動態(tài)語言,,這意味著它不僅需要一個編譯器,,也需要一個運行時系統(tǒng)來動態(tài)得創(chuàng)建類和對象、進行消息傳遞和轉(zhuǎn)發(fā),。理解 Objective-C 的 Runtime 機制可以幫我們更好的了解這個語言,,適當?shù)臅r候還能對語言進行擴展,從系統(tǒng)層面解決項目中的一些設計或技術(shù)問題,。了解 Runtime ,,要先了解它的核心 - 消息傳遞 (Messaging)。

消息傳遞(Messaging)

I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is “messaging” – that is what the kernal[sic] of Smalltalk is all about... The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviors should be.

Alan Kay 曾多次強調(diào) Smalltalk 的核心不是面向?qū)ο?,面向?qū)ο笾皇?the lesser ideas,,消息傳遞才是 the big idea

在很多語言,,比如 C ,,調(diào)用一個方法其實就是跳到內(nèi)存中的某一點并開始執(zhí)行一段代碼。沒有任何動態(tài)的特性,,因為這在編譯時就決定好了,。而在 Objective-C 中,[object foo] 語法并不會立即執(zhí)行 foo 這個方法的代碼,。它是在運行時給 object 發(fā)送一條叫 foo 的消息,。這個消息,也許會由 object 來處理,也許會被轉(zhuǎn)發(fā)給另一個對象,,或者不予理睬假裝沒收到這個消息,。多條不同的消息也可以對應同一個方法實現(xiàn)。這些都是在程序運行的時候決定的,。

事實上,,在編譯時你寫的 Objective-C 函數(shù)調(diào)用的語法都會被翻譯成一個 C 的函數(shù)調(diào)用 - objc_msgSend() 。比如,,下面兩行代碼就是等價的:

[array insertObject:foo atIndex:5];

objc_msgSend(array, @selector(insertObject:atIndex:), foo, 5);

消息傳遞的關鍵藏于 objc_object 中的 isa 指針和 objc_class 中的 class dispatch table,。

objc_object, objc_class 以及 Ojbc_method

在 Objective-C 中,類,、對象和方法都是一個 C 的結(jié)構(gòu)體,,從 objc/objc.h 頭文件中,我們可以找到他們的定義:

struct objc_object {  
    Class isa  OBJC_ISA_AVAILABILITY;
};

struct objc_class {  
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    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;
#endif
};

struct objc_method_list {  
    struct objc_method_list *obsolete;
    int method_count;

#ifdef __LP64__
    int space;
#endif

    /* variable length structure */
    struct objc_method method_list[1];
};

struct objc_method {  
    SEL method_name;
    char *method_types;    /* a string representing argument/return types */
    IMP method_imp;
};

objc_method_list 本質(zhì)是一個有 objc_method 元素的可變長度的數(shù)組,。一個 objc_method 結(jié)構(gòu)體中有函數(shù)名,,也就是SEL,有表示函數(shù)類型的字符串 (見 Type Encoding) ,,以及函數(shù)的實現(xiàn)IMP,。

從這些定義中可以看出發(fā)送一條消息也就 objc_msgSend 做了什么事。舉 objc_msgSend(obj, foo) 這個例子來說:

  1. 首先,,通過 obj 的 isa 指針找到它的 class ;
  2. 在 class 的 method list 找 foo ;
  3. 如果 class 中沒到 foo,,繼續(xù)往它的 superclass 中找 ;
  4. 一旦找到 foo 這個函數(shù),就去執(zhí)行它的實現(xiàn)IMP .

但這種實現(xiàn)有個問題,,效率低,。但一個 class 往往只有 20% 的函數(shù)會被經(jīng)常調(diào)用,可能占總調(diào)用次數(shù)的 80% ,。每個消息都需要遍歷一次 objc_method_list 并不合理,。如果把經(jīng)常被調(diào)用的函數(shù)緩存下來,那可以大大提高函數(shù)查詢的效率,。這也就是 objc_class 中另一個重要成員 objc_cache 做的事情 - 再找到 foo 之后,,把 foo 的 method_name 作為 key ,method_imp 作為 value 給存起來,。當再次收到 foo 消息的時候,,可以直接在 cache 里找到,避免去遍歷 objc_method_list.

動態(tài)方法解析和轉(zhuǎn)發(fā)

在上面的例子中,,如果 foo 沒有找到會發(fā)生什么,?通常情況下,程序會在運行時掛掉并拋出 unrecognized selector sent to … 的異常,。但在異常拋出前,,Objective-C 的運行時會給你三次拯救程序的機會:

  1. Method resolution
  2. Fast forwarding
  3. Normal forwarding

Method Resolution

首先,Objective-C 運行時會調(diào)用 +resolveInstanceMethod: 或者 +resolveClassMethod:,讓你有機會提供一個函數(shù)實現(xiàn),。如果你添加了函數(shù)并返回 YES,, 那運行時系統(tǒng)就會重新啟動一次消息發(fā)送的過程。還是以 foo 為例,,你可以這么實現(xiàn):

void fooMethod(id obj, SEL _cmd)  
{
    NSLog(@"Doing foo");
}

+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
    if(aSEL == @selector(foo:)){
        class_addMethod([self class], aSEL, (IMP)fooMethod, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod];
}

Core Data 有用到這個方法,。NSManagedObjects 中 properties 的 getter 和 setter 就是在運行時動態(tài)添加的。

如果 resolve 方法返回 NO ,,運行時就會移到下一步:消息轉(zhuǎn)發(fā)(Message Forwarding),。

PS:iOS 4.3 加入很多新的 runtime 方法,主要都是以 imp 為前綴的方法,,比如 imp_implementationWithBlock() 用 block 快速創(chuàng)建一個 imp ,。
上面的例子可以重寫成:

IMP fooIMP = imp_implementationWithBlock(^(id _self) {  
    NSLog(@"Doing foo");
});

class_addMethod([self class], aSEL, fooIMP, "v@:");  

Fast forwarding

如果目標對象實現(xiàn)了 -forwardingTargetForSelector: ,,Runtime 這時就會調(diào)用這個方法,,給你把這個消息轉(zhuǎn)發(fā)給其他對象的機會。

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if(aSelector == @selector(foo:)){
        return alternateObject;
    }
    return [super forwardingTargetForSelector:aSelector];
}

只要這個方法返回的不是 nil 和 self,,整個消息發(fā)送的過程就會被重啟,,當然發(fā)送的對象會變成你返回的那個對象。否則,,就會繼續(xù) Normal Fowarding ,。

這里叫 Fast ,只是為了區(qū)別下一步的轉(zhuǎn)發(fā)機制,。因為這一步不會創(chuàng)建任何新的對象,,但下一步轉(zhuǎn)發(fā)會創(chuàng)建一個 NSInvocation 對象,所以相對更快點,。

Normal forwarding

這一步是 Runtime 最后一次給你挽救的機會,。首先它會發(fā)送 -methodSignatureForSelector: 消息獲得函數(shù)的參數(shù)和返回值類型。如果 -methodSignatureForSelector: 返回 nil ,,Runtime 則會發(fā)出 -doesNotRecognizeSelector: 消息,,程序這時也就掛掉了。如果返回了一個函數(shù)簽名,,Runtime 就會創(chuàng)建一個 NSInvocation 對象并發(fā)送 -forwardInvocation: 消息給目標對象,。

NSInvocation 實際上就是對一個消息的描述,包括selector 以及參數(shù)等信息,。所以你可以在 -forwardInvocation: 里修改傳進來的 NSInvocation 對象,,然后發(fā)送 -invokeWithTarget: 消息給它,傳進去一個新的目標:

- (void)forwardInvocation:(NSInvocation *)invocation
{
    SEL sel = invocation.selector;

    if([alternateObject respondsToSelector:sel]) {
        [invocation invokeWithTarget:alternateObject];
    } 
    else {
        [self doesNotRecognizeSelector:sel];
    }
}

Cocoa 里很多地方都利用到了消息傳遞機制來對語言進行擴展,,如 Proxies,、NSUndoManager 跟 Responder Chain。NSProxy 就是專門用來作為代理轉(zhuǎn)發(fā)消息的;NSUndoManager 截取一個消息之后再發(fā)送,;而 Responder Chain 保證一個消息轉(zhuǎn)發(fā)給合適的響應者,。

總結(jié)

Objective-C 中給一個對象發(fā)送消息會經(jīng)過以下幾個步驟:

  1. 在對象類的 dispatch table 中嘗試找到該消息。如果找到了,,跳到相應的函數(shù)IMP去執(zhí)行實現(xiàn)代碼,;
  2. 如果沒有找到,Runtime 會發(fā)送 +resolveInstanceMethod: 或者 +resolveClassMethod: 嘗試去 resolve 這個消息,;
  3. 如果 resolve 方法返回 NO,,Runtime 就發(fā)送 -forwardingTargetForSelector: 允許你把這個消息轉(zhuǎn)發(fā)給另一個對象;
  4. 如果沒有新的目標對象返回,, Runtime 就會發(fā)送 -methodSignatureForSelector:-forwardInvocation: 消息,。你可以發(fā)送 -invokeWithTarget: 消息來手動轉(zhuǎn)發(fā)消息或者發(fā)送 -doesNotRecognizeSelector: 拋出異常。

利用 Objective-C 的 runtime 特性,,我們可以自己來對語言進行擴展,,解決項目開發(fā)中的一些設計和技術(shù)問題。下一篇文章,,我會介紹 Method Swizzling 技術(shù)以及如何利用 Method Swizzling 做 Logging,。

Reference

Message forwarding

Objective-c-messaging

The faster objc_msgSend

Understanding objective-c runtime

顧 鵬

Live with less, share with more. weibo: @no-computer

Shanghai http://

Share this post

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,,謹防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多