Effective Objective-C 讀書(shū)筆記(二) | 初步理解OC運(yùn)行時(shí)

Class 類型對(duì)象

OC本身是一種強(qiáng)類型語(yǔ)言钝的,但其運(yùn)行時(shí)功能讓它又有了動(dòng)態(tài)語(yǔ)言的特點(diǎn)猴誊。OC中對(duì)象的類型和對(duì)象所執(zhí)行的方法都是在運(yùn)行時(shí)階段進(jìn)行查找并確認(rèn)的艰赞,這種機(jī)制被稱為動(dòng)態(tài)綁定莱褒。想要弄清楚運(yùn)行時(shí)如何能夠?qū)崿F(xiàn)動(dòng)態(tài)綁定機(jī)制浴栽,首先要了解OC中對(duì)象的本質(zhì)荒叼。

OC是C語(yǔ)言的超集,所以O(shè)C中面向?qū)ο蟮墓δ茉诘讓右彩鞘褂肅語(yǔ)言來(lái)實(shí)現(xiàn)典鸡。我們?cè)贠C中使用的對(duì)象被廓,通常指的是儲(chǔ)存該對(duì)象內(nèi)存地址的一個(gè)指針變量(Java中稱為引用),因此我們?cè)贠C中聲明對(duì)象時(shí)通常使用類型名稱加一個(gè)*號(hào)萝玷,稍微了解C語(yǔ)言的人都知道*號(hào)代表該變量是一個(gè)指針變量嫁乘。OC中還有一個(gè)特殊的類型id,它可以表示通用類型的OC對(duì)象球碉,因?yàn)樗旧砭捅欢x為一種特殊的指針變量蜓斧,所以不需要在id后面再加一個(gè)*號(hào)。

NSString *someString = @"Some String";
id otherString = @"Other String";

[someString count]; // 編譯期報(bào)錯(cuò)
[otherString count]; // 運(yùn)行時(shí)報(bào)錯(cuò)

使用上述兩種方式聲明對(duì)象睁冬,在語(yǔ)法意義上其實(shí)完全相同法精,因?yàn)閷?duì)象的具體類型只在運(yùn)行時(shí)才會(huì)被確認(rèn)。唯一的區(qū)別在于痴突,如果聲明時(shí)使用了具體類型信息,編譯器會(huì)在編譯期間查找對(duì)象所能執(zhí)行的方法狼荞,找不到就會(huì)報(bào)錯(cuò)辽装;而id代表通用類型的對(duì)象,編譯器默認(rèn)它能夠執(zhí)行任何已存在的方法相味。

我們可以在蘋(píng)果官方的運(yùn)行時(shí)庫(kù)的頭文件中查看id類型的定義:

struct objc_object {
    Class isa;
};
typedef struct objc_object *id;

可以看出id本質(zhì)是一個(gè)C語(yǔ)言結(jié)構(gòu)體拾积,該結(jié)構(gòu)體只有一個(gè)Class類型的成員isa(取意is a,是一個(gè))丰涉,代表著對(duì)象所屬的具體類型拓巧。其實(shí)在NSObject類的頭文件中,同樣聲明有一個(gè)這樣的實(shí)例變量isa一死。因此肛度,可以說(shuō)OC中任何對(duì)象,都會(huì)默認(rèn)帶有一個(gè)實(shí)例變量isa用來(lái)儲(chǔ)存對(duì)象的具體類型信息投慈。

Class的定義也可以在運(yùn)行時(shí)庫(kù)的頭文件中查看:

struct objc_class {
    Class isa;
    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;
};
typedef struct objc_class *Class;

此結(jié)構(gòu)體可以儲(chǔ)存類的諸多信息承耿,例如類型名冠骄、父類類型、實(shí)例變量列表加袋、方法列表等凛辣,這些信息被稱作類的元數(shù)據(jù)(metadata)。該結(jié)構(gòu)體也有一個(gè)Class類型的成員isa职烧,說(shuō)明Class本身也是一個(gè)OC對(duì)象(被稱為類對(duì)象或類型對(duì)象)扁誓,而它的對(duì)象類型(isa所指向的類型)被稱為元類(metaclass),元類中儲(chǔ)存的是類對(duì)象的元數(shù)據(jù)蚀之,比如類方法就儲(chǔ)存在這里蝗敢。每個(gè)類可以有無(wú)數(shù)個(gè)對(duì)象,但僅有一個(gè)類對(duì)象恬总,也僅有一個(gè)與之對(duì)應(yīng)的元類前普。

對(duì)象、類對(duì)象和元類的關(guān)系如下圖所示:

由于類對(duì)象和isa指針的存在壹堰,OC中的所有對(duì)象都可以在運(yùn)行時(shí)查找自己的真實(shí)類型拭卿,并確定自己所能執(zhí)行的方法。當(dāng)真正給對(duì)象發(fā)送一條消息(或稱為調(diào)用方法)時(shí)贱纠,運(yùn)行時(shí)機(jī)制會(huì)對(duì)該消息進(jìn)行一系列復(fù)雜的處理峻厚,接下來(lái)我們就繼續(xù)討論運(yùn)行時(shí)的消息處理。

Message Dispatch 消息派發(fā)

調(diào)用對(duì)象的某個(gè)方法(或稱為給對(duì)象發(fā)送某個(gè)消息)是面向?qū)ο缶幊讨凶畛J褂玫墓δ茏缓浮T贠C中惠桃,由于動(dòng)態(tài)綁定機(jī)制使得程序直到運(yùn)行時(shí)才能清楚那個(gè)方法需要被執(zhí)行,甚至通過(guò)使用底層的運(yùn)行時(shí)函數(shù)辖试,就可以更改調(diào)用的方法或改變方法內(nèi)部的功能實(shí)現(xiàn)辜王,這些特性使得OC成為一門真正的動(dòng)態(tài)語(yǔ)言。

id returnValue = [someObject messageName:param];

OC的消息處理罐孝,在底層也是使用C語(yǔ)言函數(shù)來(lái)實(shí)現(xiàn)呐馆,與消息處理功能相對(duì)應(yīng)的函數(shù)叫做objc_msgSend,該函數(shù)在頭文件中的聲明如下:

id objc_msgSend(id self, SEL op, ...)

可以看出objc_msgSend是一個(gè)可變參數(shù)函數(shù)莲兢,其中第一個(gè)參數(shù)代表消息的接收者汹来,第二個(gè)參數(shù)代表消息的選擇器,后續(xù)參數(shù)表示消息發(fā)送時(shí)附帶的參數(shù)改艇。編譯器在編譯期間就會(huì)將發(fā)送消息的代碼轉(zhuǎn)換為objc_msgSend函數(shù)收班。

id returnValue = objc_msgSend(someObject, @selector(messageName:), param);

在運(yùn)行時(shí)階段,objc_msgSend函數(shù)內(nèi)部會(huì)根據(jù)消息的接收者和選擇器來(lái)選擇調(diào)用適當(dāng)?shù)姆椒ㄚ诵帧橥瓿纱瞬僮鳎?em>objc_msgSend函數(shù)首先會(huì)根據(jù)消息接收者對(duì)象的isa指針找到它的真實(shí)類型摔桦,然后在該類對(duì)象的方法列表中查找是否有與當(dāng)前選擇器相對(duì)應(yīng)的方法,如果有則跳轉(zhuǎn)到該方法執(zhí)行承疲;如果沒(méi)有找到酣溃,則會(huì)按照類的繼承體系向上繼續(xù)查找瘦穆,一旦找到就跳轉(zhuǎn)過(guò)去執(zhí)行目標(biāo)方法。當(dāng)最終都沒(méi)有找到與當(dāng)前選擇器相對(duì)應(yīng)的方法時(shí)赊豌,運(yùn)行時(shí)機(jī)制則會(huì)開(kāi)啟消息轉(zhuǎn)發(fā)流程扛或,我們接下來(lái)就繼續(xù)討論運(yùn)行時(shí)的消息轉(zhuǎn)發(fā)。

Message Forward 消息轉(zhuǎn)發(fā)

消息轉(zhuǎn)發(fā)流程比較復(fù)雜碘饼,主要分三個(gè)步驟熙兔,首先我們來(lái)看一張消息轉(zhuǎn)發(fā)完整的流程圖。

第一步

當(dāng)消息派發(fā)流程最終在對(duì)象的類和父類中都沒(méi)有找到對(duì)應(yīng)選擇器的方法時(shí)艾恼,就會(huì)開(kāi)啟消息轉(zhuǎn)發(fā)流程住涉。首先,第一步會(huì)先調(diào)用消息接收者所在類的resolveInstanceMethod:方法钠绍,該方法返回一個(gè)BOOL值舆声,表示是否動(dòng)態(tài)添加一個(gè)方法來(lái)響應(yīng)當(dāng)前消息選擇器。如果發(fā)送的消息是一個(gè)類方法柳爽,則會(huì)調(diào)用另一個(gè)類似的方法resolveClassMethod:媳握。

+ (BOOL)resolveInstanceMethod:(SEL)sel;
+ (BOOL)resolveClassMethod:(SEL)sel;

以上兩個(gè)方法均聲明在NSObject類中,如果消息接收者所在類重寫(xiě)了resolveInstanceMethod:方法并返回YES磷脯,也就意味著想要?jiǎng)討B(tài)添加一個(gè)方法來(lái)響應(yīng)當(dāng)前的消息選擇器蛾找,可以在重寫(xiě)的方法內(nèi)使用class_addMethod函數(shù)來(lái)為當(dāng)前類添加方法。

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

該函數(shù)第一個(gè)參數(shù)代表為哪個(gè)類添加方法赵誓,第二個(gè)參數(shù)是方法所對(duì)應(yīng)的選擇器打毛,第三個(gè)參數(shù)是C語(yǔ)言的函數(shù)指針,用來(lái)指向待添加的方法俩功,最后一個(gè)參數(shù)表示待添加方法的類型編碼(詳情可查看蘋(píng)果官方文檔:Objective-C Runtime Programming Guide)幻枉。

第二步

如果上一步過(guò)程中,并沒(méi)有新方法能響應(yīng)消息選擇器诡蜓,則會(huì)進(jìn)入消息轉(zhuǎn)發(fā)流程的第二步熬甫。在第二步中系統(tǒng)會(huì)調(diào)用當(dāng)前消息接收者所在類的forwardingTargetForSelector:方法,用以詢問(wèn)能否將該條消息發(fā)送給其他接收者來(lái)處理万牺,方法的返回值就代表這個(gè)新的接收者,如果不允許將消息轉(zhuǎn)發(fā)給其他接收者則返回nil洽腺。

- (id)forwardingTargetForSelector:(SEL)aSelector;

利用這個(gè)方法脚粟,我們可以使用組合的方式模擬出多重繼承的特性。比如可以在一個(gè)類中擁有一系列其他類型的屬性蘸朋,然后重寫(xiě)forwardingTargetForSelector:方法核无,根據(jù)這些屬性所能響應(yīng)的消息選擇器返回對(duì)應(yīng)的屬性對(duì)象,這樣在外界看起來(lái)藕坯,該類的對(duì)象就好像是能夠處理多種不同類型的方法了。

第三步

如果forwardingTargetForSelector:方法的返回值為nil,那么消息轉(zhuǎn)發(fā)機(jī)制還要繼續(xù)進(jìn)行最后一步碍舍。在這一步中酥宴,系統(tǒng)會(huì)將尚未處理的消息包裝成一個(gè)NSInvocation對(duì)象,其內(nèi)部包含與該消息相關(guān)的所有信息窑滞,比如消息的選擇器、目標(biāo)接收者、參數(shù)等局义。之后系統(tǒng)會(huì)調(diào)用消息接收者所在類的forwardInvocation:方法,并將生成的NSInvocation對(duì)象作為參數(shù)傳入冗疮。

- (void)forwardInvocation:(NSInvocation *)anInvocation;

forwardInvocation:方法同樣聲明在NSObject類中萄唇,我們可以重寫(xiě)該方法的實(shí)現(xiàn)。比如將NSInvocation對(duì)象的target屬性設(shè)置為其他接收者术幔,此操作可以實(shí)現(xiàn)與上一步操作同樣的效果另萤,但明顯在效率上沒(méi)有第二步的操作高,所以很少有人在這一步中僅僅只是改變消息的接收者诅挑。NSInvocation類中還提供了許多屬性和方法用于修改其對(duì)應(yīng)方法的信息四敞,比如可以修改方法的參數(shù)和返回值,或者直接更改消息選擇器轉(zhuǎn)而調(diào)用其他方法揍障。

如果消息接收者在這一步中仍然無(wú)法響應(yīng)消息選擇器目养,那么系統(tǒng)會(huì)自動(dòng)調(diào)用doesNotRecognizeSelector:方法,該方法默認(rèn)實(shí)現(xiàn)為拋出異常毒嫡,也就是我們?cè)陂_(kāi)發(fā)中經(jīng)常見(jiàn)到的unrecognized selector sent to instance癌蚁。

-[ViewController count]: unrecognized selector sent to instance

消息轉(zhuǎn)發(fā)示例

現(xiàn)在再回頭看我們之前消息轉(zhuǎn)發(fā)完整的流程圖,應(yīng)該能夠更清晰地了解系統(tǒng)執(zhí)行每一步操作的目的和作用了兜畸。接下來(lái)我們用一個(gè)示例來(lái)演示如何利用消息轉(zhuǎn)發(fā)機(jī)制來(lái)自定義一個(gè)字典類努释,該字典類的對(duì)象可以直接使用屬性方式來(lái)存取內(nèi)容。完整的示例代碼如下咬摇。

// WXGAutoDictionary.h
#import <Foundation/Foundation.h>

@interface WXGAutoDictionary : NSObject

// 可供存儲(chǔ)的屬性伐蒂,可以為任意OC對(duì)象
@property (nonatomic, strong) id obj;

@end

// WXGAutoDictionary.m
#import "WXGAutoDictionary.h"
#import <objc/runtime.h>

@interface WXGAutoDictionary ()

@property (nonatomic, strong) NSMutableDictionary *backStore; // 后臺(tái)存儲(chǔ)用字典

@end

@implementation WXGAutoDictionary

@dynamic obj; // 禁止編譯器自動(dòng)生成getter和setter方法

- (instancetype)init {
    if (self = [super init]) {
        _backStore = @{}.mutableCopy; // 初始化字典
    }
    return self;
}

// 重寫(xiě)此方法,允許動(dòng)態(tài)添加方法來(lái)響應(yīng)指定的消息選擇器
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSString *selString = NSStringFromSelector(sel);
    
    // 類型編碼:v->void  @->OC對(duì)象  :->SEL選擇器
    // 響應(yīng)setter方法的選擇器
    if ([selString hasPrefix:@"set"]) {
        class_addMethod(self, sel, (IMP)autoDictionarySetter, "v@:@");
    } else { // 響應(yīng)getter方法的選擇器
        class_addMethod(self, sel, (IMP)autoDictionaryGetter, "@@:");
    }
    
    return YES;
}

// 處理setter方法的函數(shù)
void autoDictionarySetter(id self, SEL sel, id value) {
    WXGAutoDictionary *autoDict = (WXGAutoDictionary *)self;
    NSMutableDictionary *backStore = autoDict.backStore;
    
    NSString *selString = NSStringFromSelector(sel);
    NSMutableString *key = selString.mutableCopy;
    [key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)];
    [key deleteCharactersInRange:NSMakeRange(0, 3)];
    [key replaceCharactersInRange:NSMakeRange(0, 1) withString:[[key substringToIndex:1] lowercaseString]];
    
    if (value) {
        [backStore setObject:value forKey:key];
    } else {
        [backStore removeObjectForKey:key];
    }
}

// 處理getter方法的函數(shù)
id autoDictionaryGetter(id self, SEL sel) {
    WXGAutoDictionary *autoDict = (WXGAutoDictionary *)self;
    NSMutableDictionary *backStore = autoDict.backStore;
    
    NSString *key = NSStringFromSelector(sel);
    return [backStore objectForKey:key];
}

@end

在外部使用該類非常簡(jiǎn)單肛鹏,示例代碼如下逸邦。

//  main.m
#import <Foundation/Foundation.h>
#import "WXGAutoDictionary.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        WXGAutoDictionary *dict = [[WXGAutoDictionary alloc] init];
        dict.obj = [NSDate date];
        
        NSLog(@"%@", dict.obj); // 控制臺(tái)輸出當(dāng)前日期
    }
    return 0;
}

在程序開(kāi)始運(yùn)行后,dict對(duì)象所在的類中并沒(méi)有響應(yīng)setter和getter選擇器的方法在扰,消息派發(fā)階段無(wú)法在類對(duì)象的方法列表中找到合適的方法缕减,所以會(huì)進(jìn)入消息轉(zhuǎn)發(fā)流程。我們?cè)?code>resolveInstanceMethod:方法中返回YES芒珠,并為不同選擇器指定了不同的方法去處理桥狡,從而實(shí)現(xiàn)通過(guò)屬性的setter和getter方法對(duì)字典進(jìn)行存取操作。當(dāng)有另一個(gè)類型的屬性需要使用同樣的功能時(shí),只需在WXGAutoDictionary類中添加屬性裹芝,并將屬性聲明為@dynamic即可部逮,屬性的存取操作會(huì)由運(yùn)行時(shí)系統(tǒng)動(dòng)態(tài)指定方法來(lái)完成。

Method Swizzing 方法調(diào)配

我們已經(jīng)了解了OC中對(duì)象的類型和消息處理機(jī)制嫂易,這些有助于我們進(jìn)一步了解OC運(yùn)行時(shí)的其他功能和特性兄朋。接下來(lái)就介紹其中一種叫做Method Swizzing(方法調(diào)配)的技術(shù),該技術(shù)經(jīng)常被稱為iOS開(kāi)發(fā)中的黑魔法炬搭。

在介紹方法調(diào)配技術(shù)之前蜈漓,我們首先來(lái)了解一下OC中方法和消息選擇器之間的關(guān)系,因?yàn)槲覀兘?jīng)常會(huì)將他們混為一談宫盔。在運(yùn)行時(shí)頭文件中融虽,我們可以找到方法的底層結(jié)構(gòu)定義。

struct objc_method {
    SEL method_name;
    char *method_types;
    IMP method_imp;
}

可以看出灼芭,每一個(gè)方法內(nèi)部都包含三個(gè)成員有额,第一個(gè)是選擇器代表方法的名字,第二個(gè)是方法的類型彼绷,其值是一個(gè)C語(yǔ)言字符串巍佑,可以參考前文講過(guò)類型編碼,最后一個(gè)是C語(yǔ)言中的函數(shù)指針寄悯,用以指向方法具體執(zhí)行的函數(shù)萤衰。我們可以把方法的內(nèi)部結(jié)構(gòu)理解為每一個(gè)SEL選擇器(可以當(dāng)做是方法名)對(duì)應(yīng)一個(gè)具體的IMP函數(shù)(可以當(dāng)做是方法的實(shí)現(xiàn)),這也是SEL被稱為選擇器的原因猜旬。這樣我們就可以更加清楚地理解消息派發(fā)時(shí)脆栋,系統(tǒng)是如何根據(jù)消息選擇器來(lái)查找對(duì)應(yīng)的方法并跳轉(zhuǎn)到方法的具體實(shí)現(xiàn)的了。

首先洒擦,當(dāng)對(duì)象接收到某個(gè)消息時(shí)椿争,編譯器首先將代碼轉(zhuǎn)換為objc_msgSend函數(shù),并將消息的接收者和選擇器當(dāng)做函數(shù)的參數(shù)傳入熟嫩,接下來(lái)系統(tǒng)會(huì)根據(jù)接收者的isa指針找到它所對(duì)應(yīng)的類秦踪,在類的元數(shù)據(jù)信息中找到該類所擁有的方法列表,然后遍歷方法列表掸茅,將每一個(gè)方法內(nèi)部的SEL選擇器同傳入的消息選擇器進(jìn)行匹配椅邓,當(dāng)找到相同的選擇器后,就根據(jù)方法內(nèi)部的IMP函數(shù)指針跳轉(zhuǎn)到方法的具體實(shí)現(xiàn)昧狮。當(dāng)然景馁,為了提高方法多次執(zhí)行的效率,系統(tǒng)會(huì)將遍歷查詢的結(jié)果緩存起來(lái)陵且,儲(chǔ)存在類的元數(shù)據(jù)信息中裁僧,此處就不再繼續(xù)深入討論。

了解清楚選擇器和方法實(shí)現(xiàn)之間的一對(duì)一關(guān)系后慕购,我們接下來(lái)開(kāi)始介紹方法調(diào)配技術(shù)聊疲,它其實(shí)就是利用運(yùn)行時(shí)提供的函數(shù)來(lái)動(dòng)態(tài)修改選擇器和方法實(shí)現(xiàn)之間的對(duì)應(yīng)關(guān)系的一種技術(shù)。利用這種技術(shù)沪悲,我們可以在運(yùn)行時(shí)為某個(gè)類添加選擇器或更改選擇器所對(duì)應(yīng)的方法實(shí)現(xiàn)获洲,甚至可以更換兩個(gè)已有選擇器所對(duì)應(yīng)的方法實(shí)現(xiàn),從而實(shí)現(xiàn)一種極其詭異的效果殿如。下面就寫(xiě)一段示例程序贡珊,通過(guò)方法調(diào)配技術(shù)來(lái)更換NSString類的大小寫(xiě)轉(zhuǎn)換方法的實(shí)現(xiàn)(僅供娛樂(lè)使用)。

//  main.m
#import <Foundation/Foundation.h>
#import <objc/runtime.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Method lowercase = class_getInstanceMethod([NSString class], @selector(lowercaseString));
        Method uppercase = class_getInstanceMethod([NSString class], @selector(uppercaseString));
        
        method_exchangeImplementations(lowercase, uppercase);
        
        NSLog(@"%@ -- %@", [@"AbCd" lowercaseString], [@"AbCd" uppercaseString]);
        // 輸出結(jié)果:ABCD -- abcd
    }
    return 0;
}

可以看到lowercaseString方法返回的是大寫(xiě)字母涉馁,而uppercaseString方法返回了小寫(xiě)字母门岔。

方法調(diào)配技術(shù)的作用肯定不在于此,那么開(kāi)發(fā)者通常如何使用這種技術(shù)呢烤送?在總結(jié)方法調(diào)配技術(shù)的用處之前寒随,我們先再來(lái)看一個(gè)示例程序。同樣以NSString類為例帮坚,我們?yōu)槠?code>lowercaseString方法增加一些日志輸出功能(不改變方法名妻往,只是更改方法的實(shí)現(xiàn))。你可能第一時(shí)間想到用繼承來(lái)實(shí)現(xiàn)該需求试和,然而當(dāng)項(xiàng)目中有多個(gè)類需要同樣需求時(shí)讯泣,你需要每個(gè)類都去繼承一下,然后還要保證別人都是去用你的子類而不是原本的父類阅悍,這樣顯然并不是一種很好的解決辦法好渠。此時(shí)我們就可以嘗試使用方法調(diào)配技術(shù),完整的示例代碼如下溉箕。

//  NSString+Logging.h
#import <Foundation/Foundation.h>

@interface NSString (Logging)

- (NSString *)lowercaseStringWithLogging;

@end

//  NSString+Logging.m
#import "NSString+Logging.h"

@implementation NSString (Logging)

- (NSString *)lowercaseStringWithLogging {
    NSString *lowercaseString = [self lowercaseStringWithLogging];
    NSLog(@"%@ -> %@", self, lowercaseString);
    return lowercaseString;
}

//  main.m
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "NSString+Logging.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Method lowercase = class_getInstanceMethod([NSString class], @selector(lowercaseString));
        Method lowercaselogging = class_getInstanceMethod([NSString class], @selector(lowercaseStringWithLogging));
        
        method_exchangeImplementations(lowercase, lowercaselogging);
        
        [@"AbCd" lowercaseString];
        // 輸出結(jié)果:AbCd -> abcd
    }
    return 0;
}
@end

我們?yōu)镹SString類添加一個(gè)分類晦墙,在分類中添加一個(gè)帶日志輸出功能的方法肴茄,注意在該方法的實(shí)現(xiàn)中,我們調(diào)用了這句代碼[self lowercaseStringWithLogging]寡痰,這看上去應(yīng)該會(huì)使程序陷入死循環(huán),但不要忘了拦坠,我們?cè)?em>main方法中利用方法調(diào)配技術(shù)來(lái)交換原有類的方法和分類方法的實(shí)現(xiàn),所以這句代碼實(shí)際上執(zhí)行的是原本的類中的實(shí)現(xiàn)贞滨,并不會(huì)造成死循環(huán)拍棕。

通過(guò)上文的示例程序勺良,我們可以為那些完全不知道具體實(shí)現(xiàn)的方法(也稱為黑盒方法)增加日志輸出功能,這常用于程序的調(diào)試尚困。實(shí)際上,還有很多與此類似的需求事甜,既要增加功能谬泌,又需要與原有方法聯(lián)系很緊密,例如增加權(quán)限驗(yàn)證和緩存功能逻谦,這類需求常被人們稱為Aspect(切面)掌实,與之對(duì)應(yīng)的編程概念叫做Aspect Oriented Programming(面向切面編程)。面向切面編程的概念有許多優(yōu)點(diǎn)邦马,它將那些瑣碎的事物從主邏輯中分離出來(lái)潮峦,并將它們附加在與主邏輯相對(duì)應(yīng)的橫向切面中連帶執(zhí)行,是對(duì)面向?qū)ο缶幊痰囊环N補(bǔ)充勇婴。在OC中忱嘹,我們可以利用運(yùn)行時(shí)特性和方法調(diào)配技術(shù)來(lái)實(shí)現(xiàn)這類面向切面編程的需求。

寫(xiě)在最后

本文主要是自己整理的讀書(shū)筆記耕渴,并重新整理歸納拘悦,內(nèi)容只是OC運(yùn)行時(shí)的一部分,只為理順OC運(yùn)行時(shí)的基本概念橱脸,從而為理解其他運(yùn)行時(shí)特性打下基礎(chǔ)础米。比如OC中經(jīng)常使用的KVC和KVO,在理解本文這些運(yùn)行時(shí)基本概念后添诉,應(yīng)該更有助于理解它們的實(shí)現(xiàn)原理屁桑,感興趣的可以參考以下文章:

還有OC運(yùn)行時(shí)中的Associated Object(關(guān)聯(lián)對(duì)象)概念,可以參考以下文章:

其他參考閱讀:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末栏赴,一起剝皮案震驚了整個(gè)濱河市蘑斧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌须眷,老刑警劉巖竖瘾,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異花颗,居然都是意外死亡捕传,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門扩劝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)庸论,“玉大人职辅,你說(shuō)我怎么就攤上這事∧羰荆” “怎么了罐农?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵催什,是天一觀的道長(zhǎng)蒲凶。 經(jīng)常有香客問(wèn)我拆内,道長(zhǎng)麸恍,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任刻肄,我火速辦了婚禮敏弃,結(jié)果婚禮上麦到,老公的妹妹穿的比我還像新娘欠肾。我一直安慰自己,他們只是感情好刺桃,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布瑟慈。 她就那樣靜靜地躺著,像睡著了一般封豪。 火紅的嫁衣襯著肌膚如雪吹埠。 梳的紋絲不亂的頭發(fā)上疮装,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天廓推,我揣著相機(jī)與錄音樊展,去河邊找鬼专缠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛涝婉,可吹牛的內(nèi)容都是我干的墩弯。 我是一名探鬼主播渔工,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼桥温,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼策治!你這毒婦竟也來(lái)了通惫?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤珊燎,失蹤者是張志新(化名)和其女友劉穎悔政,沒(méi)想到半個(gè)月后谋国,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體迁沫,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年缅糟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窗宦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片二鳄。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泥从,死狀恐怖躯嫉,靈堂內(nèi)的尸體忽然破棺而出祈餐,到底是詐尸還是另有隱情哄陶,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站至扰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏阶祭。R本人自食惡果不足惜直秆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望圾结。 院中可真熱鬧,春花似錦培他、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)梯醒。三九已至腌紧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間号胚,已是汗流浹背猫胁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工弃秆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留菠赚,地道東北人郑藏。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓译秦,卻偏偏與公主長(zhǎng)得像筑悴,于是被迫代替她去往敵國(guó)和親阁吝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容