本文為入門介紹步做,希望能讓第一次接觸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)朗。
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ù)。
用一圖以助理解:
另外颁独,可參考: 重識(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
寨辩,Category
,objc_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)指巡。
畫了一張示意圖幫助理解:
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
秀睛, SEL
和IMP
有什么區(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ì)有更好的了解套么。