Objective-C RunTime概覽

本文為入門介紹步做,希望能讓第一次接觸Runtime概念的朋友有一個(gè)概貌了解。

一篇文章奈附,不可能講完Runtime的全部全度,但是,分成很多篇講斥滤,又有點(diǎn)「見(jiàn)樹木不見(jiàn)森林」的迷糊感覺(jué)——自己就是看了很多關(guān)于Runtime的文章将鸵,看完還是「迷霧重重」(當(dāng)然佑颇,也可能因?yàn)橘Y質(zhì)太過(guò)平庸)。

所以漩符,這一篇,盡量涉及凸克。

一句話概括

什么是Runtime闷沥?作以下引述。但也不要太奢望看完這些說(shuō)明后舆逃,就會(huì)豁然開(kāi)朗。

官方文檔Objective-C Runtime

The Objective-C runtime is a runtime library that provides support for the dynamic properties of the Objective-C language, and as such is linked to by all Objective-C apps.

Objective-C的runtime是一個(gè)「運(yùn)行時(shí)庫(kù)」虫啥,為OC這門語(yǔ)言提供動(dòng)態(tài)的特性奄妨,所有OC應(yīng)用程序都與之相關(guān)聯(lián)。

The down low on Objective-C Runtime

The Objective-C Runtime is an open source library written in C and Assembler that adds the Object Oriented capabilities to C to create the Objective-C language.

Objective-C的Runtime评雌,是一個(gè)用C和匯編寫的「開(kāi)源庫(kù)」,它為C添加了面向?qū)ο蟮奶匦跃岸瑥亩删土薕bjrctive-C這門語(yǔ)言砂轻。

The Objective-C languages defers as many decisions as it can from compile time and link time to runtime. Whenever possible, it does things dynamically. This means that the language requires not just a compiler, but also a runtime system to execute the compiled code. The runtime system acts as a kind of operating system for the Objective-C language, it’s what makes the language work.

Objective-C可以從『編譯時(shí)』、『鏈接時(shí)』再到『運(yùn)行時(shí)』斤吐,hold住盡可能多的決策搔涝。只要有可能,它都是動(dòng)態(tài)地干活兒的曲初。這就意味著体谒,這門語(yǔ)言不僅需要一個(gè)編譯器,還需要一個(gè)runtime系統(tǒng)臼婆,用來(lái)執(zhí)行編譯的代碼抒痒。這個(gè)runtime系統(tǒng)就好比如是Objective-C的「操作系統(tǒng)」,(runtime系統(tǒng))讓這門語(yǔ)言能工作起來(lái)颁褂。

簡(jiǎn)單點(diǎn)理解故响,Runtime就是一個(gè)C和匯編寫的代碼庫(kù)——是Objective-C之所以成為Objective-C的一個(gè)庫(kù)。

用一圖以助理解:

Runtime概覽

另外颁独,可參考: 重識(shí) Objective-C Runtime - Smalltalk 與 C 的融合

Runtime的三個(gè)頭文件

Runtime這個(gè)庫(kù)是開(kāi)源的彩届。有興(能)趣(力)的朋友可以仔細(xì)研究。

而平時(shí)我們會(huì)用到的Runtime函數(shù)誓酒,基本上在runtime.h, objc.h, message.h這三個(gè)頭文件中樟蠕。代碼2500行+(主要是runtime.h)

runtime.h

runtime.h中定義了若干「類型(Types)」和「函數(shù)(Functions)」。

有我們比較熟悉的Method靠柑,Ivar寨辩,Categoryobjc_property_t歼冰,objc_class類型靡狞,都在這里定義。

另外還有106個(gè)函數(shù)隔嫡。如常見(jiàn)的:

object_copy(), class_respondsToSelector(), class_copyMethodList等都在這里面甸怕。

objc.h

objc.h中定義了Class, id, SEL, IMP類型。

另外還有6個(gè)函數(shù)式曲。

message.h

聲明了一系列的方法執(zhí)行函數(shù)吝羞。

objc_msgSend()钧排、objc_msgSendSuper()都定義在這里。

名詞解釋

isa

isa是一個(gè)指針糟袁,隱式地存在于實(shí)例對(duì)象项戴、類中周叮,對(duì)象的isa指針指向所屬類——因此實(shí)例對(duì)象能知道自己屬于哪個(gè)類仿耽;類的isa指針指向一個(gè)叫「元類(Meta Class))」的玩意兒。

isa指針在三個(gè)地方有定義:

  • objc_class結(jié)構(gòu)體有聲明开缎,指向類的meta類啥箭。(在runtime.h)
  • objc_object結(jié)構(gòu)體有聲明急侥,指向?qū)ο笏鶎兕?在objc.h)
  • NSObject類有有聲明,指向?qū)ο笏鶎兕悾?在NSObject.h)

Class

Class定義在objc.h中第37铝宵、38行尊蚁,是一個(gè)指向objc_class結(jié)構(gòu)體的指針横朋。

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

So, Class就是一個(gè)「指針變量」琴锭。

objc_class結(jié)構(gòu)體在runtime.h第55-70行中有定義:

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;// isa指針, 指向一個(gè)meta類,側(cè)面印證:「類也是對(duì)象」

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;// 指向父類
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;// 變量列表
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;// 方法列表, 注意是有兩個(gè)星號(hào)的
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;// 用于緩存方法
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;// 協(xié)議
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

這個(gè)結(jié)構(gòu)體地回,包括:isa指針、父類指針绎速、類名纹冤、成員變量萌京、方法列表知残、緩存以及協(xié)議列表等求妹。

解讀一下部分成員:

isa指針
上面介紹isa的時(shí)候,說(shuō)過(guò)類也有一個(gè)isa指針净神,我們可以理解為:類本身也是一個(gè)對(duì)象——「類對(duì)象」。是「元類(Meta Class)」的實(shí)例(每個(gè)類的isa指針指向元類)爱榕。

我們熟知的「類方法」,也可以理解為是「類對(duì)象」的實(shí)例方法八匠。

而這些「元類(Meta Class)」則是「根源類(Root Meta Class)」的實(shí)例——所有元類的isa指針最終都指向根元類。根元類的isa指針指向自己抡四,最終完成閉環(huán)指巡。

畫了一張示意圖幫助理解:

isa的指針的指向

struct objc_ivar_list
struct objc_ivar_list(ivars)藻雪,是實(shí)例變量列表,保存類所聲明的所有實(shí)例變量蹋偏。

objc_method_list
struct objc_method_list(methodLists)是方法列表枢纠,給某個(gè)對(duì)象發(fā)送消息晋渺,就是來(lái)這個(gè)列表中查找是否有相應(yīng)方法實(shí)現(xiàn)的些举。

可以動(dòng)態(tài)修改methodLists的值來(lái)添加成員方法户魏,這也是Category的實(shí)現(xiàn)原理关翎。

struct objc_cache
struct objc_cache(cache)纵寝,用于緩存方法,調(diào)用過(guò)的方法會(huì)緩存到這里室奏,方便以后索引胧沫,提高速度绒怨。

Method

定義在runtime.h第44行南蹂,表示一個(gè)方法碎紊。

/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;

objc_method結(jié)構(gòu)體存儲(chǔ)了方法名仗考、方法類型和方法實(shí)現(xiàn)秃嗜。

SEL

定義在objc.h第49、50行中必搞,表示一個(gè)方法選擇器(可以簡(jiǎn)單點(diǎn)恕洲,理解為方法名霜第,一個(gè)C語(yǔ)言的字符串)泌类。

/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;

IMP

定義在objc.h第54行弹砚,表示一個(gè)方法的實(shí)現(xiàn)迅栅。由這個(gè)函數(shù)指針決定最終執(zhí)行哪段代碼为流。

/// A pointer to the function of a method implementation. 
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
#endif

Method 秀睛, SELIMP有什么區(qū)別蹂安?

  • Method:表示一個(gè)方法,本質(zhì)是一個(gè)指向objc_method結(jié)構(gòu)體的指針允瞧。
  • SEL(Selector):在運(yùn)行時(shí)用來(lái)代表一個(gè)方法的名字述暂。
  • IMP(Implementation):表示方法的實(shí)現(xiàn)部分。第一個(gè)參數(shù)id指向調(diào)用方法的自身艺配,第二個(gè)參數(shù)是方法的名字seletor绳锅,方法的參數(shù)緊隨其后鳞芙。

在消息發(fā)送的過(guò)程中,這三個(gè)概念是可以互相轉(zhuǎn)換的喳坠。

可以這樣理解:

Runtime中壕鹉,Class維護(hù)了一份分發(fā)列表(dispatch table),用于消息分發(fā)脊凰;列表中每個(gè)入口,就是一個(gè)方法(Method)帕胆,這份列表的key是selector(SEL),value是implementation(IMP)歼捐。

而后面介紹到的Method Swizzling豹储,就是改變這份列表某兩個(gè)方法的SEL和IMP的對(duì)應(yīng)關(guān)系巩剖,讓seletor對(duì)應(yīng)一個(gè)不同的implementation钠怯。

(也有人比喻:SLE是門牌號(hào)碼佳魔,IMP是住戶)

id

定義在objc.h第45、46行中晦炊,表示一個(gè)類的實(shí)例對(duì)象鞠鲜。

/// A pointer to an instance of a class.
typedef struct objc_object *id;

objc_object這個(gè)結(jié)構(gòu)體,定義在objc.h中断国,這個(gè)結(jié)構(gòu)體只有一個(gè)指向類的isa指針贤姆。

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

Ivar

定義在runtime.h第44行,表示一個(gè)實(shí)例變量稳衬。

/// An opaque type that represents an instance variable.
typedef struct objc_ivar *Ivar;

Cache

定義在runtime.h第1841行街夭。

typedef struct objc_cache *Cache OBJC2_UNAVAILABLE;

Cache的存在檐什,是為方法調(diào)用時(shí)的性能優(yōu)化:實(shí)例對(duì)象收到消息后,會(huì)先從Cache中查找挠轴,看是否有方法的實(shí)現(xiàn)——Runtime會(huì)把調(diào)用過(guò)的方法緩存到Cache中邢隧。

objc_property_t

定義在runtime.h第52院峡,53行。

/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;

表示Objective-C中的屬性。

Category

runtime.h第49,50行:

/// An opaque type that represents a category.
typedef struct objc_category *Category;

objc_category結(jié)構(gòu)體定義在第1784-1790行毛好。

struct objc_category {
    char * _Nonnull category_name                            OBJC2_UNAVAILABLE;
    char * _Nonnull class_name                               OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable instance_methods     OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable class_methods        OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;

Category可以動(dòng)態(tài)的地為已存在的類添加新的方法。

self & super

先做個(gè)實(shí)驗(yàn):

打印:

NSLog(@"[self class]:%@; [super class]:%@", NSStringFromClass([self class]), NSStringFromClass([super class]));

結(jié)果哮兰,[self class][super class]的值是一樣的。 Why?不應(yīng)該打印一個(gè)子類远剩,一個(gè)父類嗎?

self蛔钙,是一個(gè)隱藏參數(shù)遍希,隱藏在objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)函數(shù)中,發(fā)送的所有方法演训,第一個(gè)參數(shù)都是self眯娱。

super不是隱藏參數(shù)蚤蔓,是一個(gè)「編譯器標(biāo)示符」洼专,它告訴編譯器叹俏,調(diào)用父類的方法唆貌,而不是本類的方法蓖宦。但是电爹,這時(shí)候?qū)嶋H上的消息的接收者摇邦,還是self。

詳解解讀:

  • 執(zhí)行[super class]产喉,會(huì)先調(diào)用objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)函數(shù)痹换;

  • 再根據(jù)objc_super結(jié)構(gòu)體的super_class去查找方法實(shí)現(xiàn),都弹,最后調(diào)用objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)執(zhí)行方法的實(shí)現(xiàn)娇豫。 所以,最后的接收器還是self畅厢。

因此冯痢,上述打印結(jié)果的值是一樣的。

消息的傳遞流程

關(guān)于OC中的消息傳遞流程框杜,畫了一張圖以幫助理解(流程由下往上):

消息傳遞流程

Objective-C的消息傳遞流程浦楣,個(gè)人劃分為三部分

  • 正常的消息傳遞(Messaging)
  • 消息動(dòng)態(tài)解析(Dynamic Method Resolution)
  • 消息轉(zhuǎn)發(fā)(Message Forwarding)又分2小步:
    • Fast forwarding
    • Normal forwarding

第一部分,叫做「正常的消息傳遞」霸琴,那理所當(dāng)然椒振,后面的就是「不正常」了梧乘。事實(shí)是:如果能找到方法的實(shí)現(xiàn)(IMP/implementation)澎迎,就不會(huì)跳到后面。

Runtime應(yīng)用

1.獲取類的相關(guān)情況

比如选调,我想創(chuàng)建一個(gè)類似UITableView的類夹供,然后打算參考一下官方的這個(gè)類都聲明了哪些方法,可以用以下方式查看(頭文件聲明的方法并不是全部方法):

    /* 獲取某個(gè)類的方法列表(所有方法) */
    // 這樣獲取是實(shí)例方法(不是類方法)
    Method *methods = class_copyMethodList([UITableView class], &outCount);
    for (NSUInteger methodIndex = 0 ; methodIndex < outCount; methodIndex ++) {
        SEL name = method_getName(methods[methodIndex]);
        NSLog(@"Human-例法方實(shí)-%@",NSStringFromSelector(name));
    }

還有很多其他函數(shù):class_getInstanceVariable(), objc_getMetaClass(), class_getClassVariable()等等仁堪。

2.動(dòng)態(tài)添加方法的實(shí)現(xiàn)

比如哮洽,我們用了某個(gè)閉源的框架,不幸地弦聂,有個(gè)bug是:某方法沒(méi)有實(shí)現(xiàn)鸟辅,導(dǎo)致crash:

[Animal jump]: unrecognized selector sent to instance

這時(shí)候如果等閉源框架的debug更新,比較被動(dòng)莺葫。而利用Runtime匪凉,可以動(dòng)態(tài)地添加方法的實(shí)現(xiàn),防止crash:

#import "Bird.h"
#import <objc/runtime.h>

// 創(chuàng)建Animal的子類Bird
@implementation Bird

// 如果沒(méi)有找到實(shí)例方法的實(shí)現(xiàn), 就會(huì)回調(diào)跳到這里
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(jump)) {
        // 利用Runtime的class_addMethod()函數(shù), 動(dòng)態(tài)添加方法的實(shí)現(xiàn)
        class_addMethod(self, sel, (IMP)jumpImp, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

void jumpImp(id obj, SEL _cmd) {
    NSLog(@"執(zhí)行了jumpImp(動(dòng)態(tài)添加的方法實(shí)現(xiàn))");
}

@end

3.Method Swizzling

Method Swizzling捺檬,可以理解為「交換方法的實(shí)現(xiàn)(IMP)」再层,這是網(wǎng)友的說(shuō)法,官方并沒(méi)有這種說(shuō)法堡纬,可見(jiàn)蘋果官方應(yīng)該是不提倡這樣做的聂受。

假如有個(gè)需求:需要記錄App每個(gè)頁(yè)面進(jìn)入的次數(shù)(這個(gè)需求和Method Swizzling介紹的一樣)

我們可以在viewWillAppear:方法中作一些計(jì)數(shù)處理。但是烤镐,每個(gè)頁(yè)面都要寫重復(fù)的代碼蛋济。在這里就可以使用Method Swizzling,「動(dòng)態(tài)地」在官方的基礎(chǔ)上增加一些代碼炮叶,以實(shí)現(xiàn)需求碗旅。

需要新建一個(gè)UIViewController的Category鹊杖,在load方法中實(shí)現(xiàn)互換。

#import "UIViewController+Tracking.h"
#import <objc/runtime.h>

@implementation UIViewController (Tracking)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        
        // 拿到兩個(gè)Method對(duì)象
        SEL originalSelector = @selector(viewWillAppear:);
        SEL swizzledSelector = @selector(antony_viewWillAppear:);
        
        Method orignalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        
        if (didAddMethod == YES) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(orignalMethod), method_getTypeEncoding(orignalMethod));
        }
        else {
            // 利用method_exchangeImplementations()函數(shù)交換兩個(gè)Method的實(shí)現(xiàn)
            method_exchangeImplementations(orignalMethod, swizzledMethod);
        }
    });
}

#pragma mark - Method Swizzling
- (void)antony_viewWillAppear:(BOOL)animated {
    // 因?yàn)榛Q了方法, 這里實(shí)際調(diào)用的是viewWillAppear:的IMP(不會(huì)造成遞歸)
    [self antony_viewWillAppear:animated];

    // 在這里增加你要的功能
    NSLog(@"這是在viewWillAppear:新增的內(nèi)容");
}

@end

核心就是用method_exchangeImplementations()函數(shù)扛芽,互換了viewWillAppear:和antony_viewWillAppear:的實(shí)現(xiàn)。

而如果現(xiàn)在創(chuàng)建控制器對(duì)象积瞒,實(shí)際流程是這樣的:

  • viewWillAppea:被執(zhí)行(實(shí)際上執(zhí)行上述Category的antony_viewWillAppear:方法)
  • antony_viewWillAppear:方法內(nèi)又調(diào)用了antony_viewWillAppear:(實(shí)際上執(zhí)行的是系統(tǒng)的viewWillAppear:方法——因?yàn)榛Q了)
  • 最后再執(zhí)行我們自己添加的代碼——這樣就實(shí)現(xiàn)了需求:所有UIViewController在執(zhí)行 viewWillAppear:時(shí), 都會(huì)調(diào)用你增加的代碼川尖。從而無(wú)須在所有的UIViewController中重復(fù)寫這部分代碼。

Github有個(gè)框架:Aspects茫孔,就是用Runtime的Method Swizzling實(shí)現(xiàn)的叮喳,它允許你往任意現(xiàn)存類或?qū)嵗砑宇~外的代碼。

4.動(dòng)態(tài)添加屬性 - 利用Associated Objects(Associative References)

Associative References(關(guān)聯(lián)引用/對(duì)象)缰贝,在runtime.h中定義的三個(gè)相關(guān)函數(shù):

  • objc_setAssociatedObject()
  • objc_getAssociatedObject()
  • objc_removeAssociatedObjects()

有什么作用呢馍悟?

網(wǎng)上有種說(shuō)法:OC中的Category不能添加屬性。

其實(shí)嚴(yán)格來(lái)說(shuō):Category不能添加的是「實(shí)例變量」剩晴,而屬性其實(shí)是可以添加的:

  • 不能為Category添加實(shí)例變量锣咒;否則報(bào)錯(cuò):Instance variables may not be placed in categories

  • 但是可以為Category添加屬性,也可以自定義setter赞弥、getter毅整,外部也可以訪問(wèn);但是绽左,這個(gè)屬性是無(wú)意義的悼嫉,因?yàn)椴荒鼙4鏀?shù)據(jù)(可以返回值,但是不能賦值)拼窥。而不能保存數(shù)據(jù)的原因戏蔑,是因?yàn)闆](méi)有實(shí)例變量「裝」數(shù)據(jù);

而Associated Objects(關(guān)聯(lián)對(duì)象)鲁纠,則可以為Category提供保存數(shù)據(jù)的地方总棵。

因此Associated Objects(關(guān)聯(lián)對(duì)象)就可以:給已有類(封閉的類)添加真正有意義的屬性——可以保存數(shù)據(jù)的屬性。

比如房交,我們要為一個(gè)叫做Human的類添加一個(gè)屬性nickName彻舰,就可以:

#import "Human+AdditionalProperties.h"
#import <objc/runtime.h>

@implementation Human (AdditionalProperties)
@dynamic nickName;

// 如果要?jiǎng)h除該屬性,調(diào)用objc_setAssociatedObject()賦值為nil即可,
// 不要用objc_removeAssociatedObjects(), 該函數(shù)會(huì)刪除所有添加的屬性
- (void)setNickName:(NSString *)nickName {
    // 參數(shù)1: 為哪個(gè)對(duì)象實(shí)現(xiàn)的關(guān)聯(lián)
    // 參數(shù)2: 這個(gè)關(guān)聯(lián)的key(可以用SEL作為key)
    // 參數(shù)3: 需要與對(duì)應(yīng)key(參數(shù)2)關(guān)聯(lián)的值(就是外部傳入的值)
    // 參數(shù)4: 關(guān)聯(lián)的策略(和屬性的attribute相對(duì)應(yīng))
    objc_setAssociatedObject(self, @selector(nickName), nickName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)nickName {
    // 參數(shù)1: 為哪個(gè)對(duì)象實(shí)現(xiàn)的關(guān)聯(lián)
    // 參數(shù)2: 該關(guān)聯(lián)的key
    return objc_getAssociatedObject(self, @selector(nickName));
}
@end

需要再次強(qiáng)調(diào)的是:通過(guò)Associated Objects為類添加有意義的屬性,事實(shí)上并不是添加了實(shí)例變量候味,而是通過(guò)關(guān)聯(lián)刃唤,使屬性有保存數(shù)據(jù)的能力。(可以用class_copyPropertyList()驗(yàn)證白群,并沒(méi)有增加實(shí)例變量尚胞。或者斷點(diǎn)看該類的實(shí)例帜慢,并不會(huì)看到有添加了實(shí)例變量——雖然能用該屬性來(lái)存取數(shù)據(jù)笼裳。)

5.歸檔和解檔 一鍵序列化:

有用過(guò)NSKeyedArchiver固化自定義對(duì)象到沙盒的朋友應(yīng)該了解唯卖,當(dāng)一個(gè)自定義對(duì)象有很多屬性,需要一個(gè)一個(gè)encode(編碼)或者decode(解碼)躬柬,是很瑣屑的拜轨,比如:

自定義類有很多屬性

而利用Runtime,則可以簡(jiǎn)化這個(gè)過(guò)程——無(wú)論類有多少屬性:

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super init];
    if (self) {
        unsigned int count = 0;
        // 利用class_copyIvarList()拿到類的所有實(shí)例變量
        Ivar *ivars = class_copyIvarList([self class], &count);
        // 再用for循環(huán)一次性解檔
        for (int i = 0; i < count; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            id value = [aDecoder decodeObjectForKey:key];
            [self setValue:value forKey:key];
        }
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    unsigned int count = 0;
    // 利用class_copyIvarList()拿到類的所有實(shí)例變量
    Ivar *ivars = class_copyIvarList([self class], &count);
    // 再用for循環(huán)一次性歸檔
    for (int i = 0; i < count; i++) {
        Ivar ivar = ivars[i];
        NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
        id object = [self valueForKey:key];
        [aCoder encodeObject:object forKey:key];
    }
}

Runtime還有很多應(yīng)用允青,有興趣可以繼續(xù)找相關(guān)資料學(xué)習(xí)橄碾。不過(guò):

Objective-C的Runtime就像一把雙刃劍,使用它颠锉,風(fēng)險(xiǎn)高法牲,回報(bào)也高。它賦予你很大的權(quán)力琼掠,但只要你犯了哪怕一丁點(diǎn)兒錯(cuò)誤拒垃,都有可能讓程序掛掉。

所以瓷蛙,總原則:能不用悼瓮,盡量不用。

Conclusion

到這里艰猬,估計(jì)還是有很多黑人問(wèn)號(hào):Runtime究竟是什么玩意兒谤牡? What the hell is Runtime??

這很正常姥宝,學(xué)習(xí)本來(lái)就是一個(gè)重復(fù)的過(guò)程——特別是面對(duì)學(xué)習(xí)曲線還比較陡峭的知識(shí)翅萤。繼續(xù)實(shí)踐、溫故知新腊满,相信后面會(huì)有更好的了解套么。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市碳蛋,隨后出現(xiàn)的幾起案子胚泌,更是在濱河造成了極大的恐慌,老刑警劉巖肃弟,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件玷室,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡笤受,警方通過(guò)查閱死者的電腦和手機(jī)穷缤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)箩兽,“玉大人津肛,你說(shuō)我怎么就攤上這事『蛊叮” “怎么了身坐?”我有些...
    開(kāi)封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵秸脱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我部蛇,道長(zhǎng)摊唇,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任涯鲁,我火速辦了婚禮遏片,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘撮竿。我一直安慰自己,他們只是感情好笔呀,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布幢踏。 她就那樣靜靜地躺著,像睡著了一般许师。 火紅的嫁衣襯著肌膚如雪房蝉。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天微渠,我揣著相機(jī)與錄音搭幻,去河邊找鬼。 笑死逞盆,一個(gè)胖子當(dāng)著我的面吹牛檀蹋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播云芦,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼俯逾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了舅逸?” 一聲冷哼從身側(cè)響起桌肴,我...
    開(kāi)封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎琉历,沒(méi)想到半個(gè)月后坠七,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡旗笔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年彪置,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蝇恶。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悉稠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出艘包,到底是詐尸還是另有隱情的猛,我是刑警寧澤耀盗,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站卦尊,受9級(jí)特大地震影響叛拷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜岂却,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一忿薇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧躏哩,春花似錦署浩、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至正驻,卻和暖如春弊攘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背姑曙。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工襟交, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人伤靠。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓捣域,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親宴合。 傳聞我的和親對(duì)象是個(gè)殘疾皇子竟宋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,679評(píng)論 0 9
  • objc_getAssociatedObject返回與給定鍵的特定對(duì)象關(guān)聯(lián)的值形纺。ID objc_getAssoci...
    有一種再見(jiàn)叫青春閱讀 1,564評(píng)論 0 7
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 727評(píng)論 0 2
  • 本文詳細(xì)整理了 Cocoa 的 Runtime 系統(tǒng)的知識(shí)丘侠,它使得 Objective-C 如虎添翼,具備了靈活的...
    lylaut閱讀 792評(píng)論 0 4
  • 我們常常會(huì)聽(tīng)說(shuō) Objective-C 是一門動(dòng)態(tài)語(yǔ)言逐样,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢蜗字?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,172評(píng)論 0 7