老樣子..先看文檔 知道 runtime 到底是個(gè)什么東西呢.....
The Objective-C language 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.
runtime 使objc變成了真正的動(dòng)態(tài)語言,是objc 的操作系統(tǒng).objc 延遲了許多決定,直到運(yùn)行時(shí)才做出.runtime 給了 objc 真正的動(dòng)態(tài)性
runtime 運(yùn)行時(shí)有兩個(gè)版本,一般手機(jī)和64位機(jī)器的用的是現(xiàn)代版本,一般32位的機(jī)器用的是古老版本..我們接下來都是以現(xiàn)代版本進(jìn)行解釋的
和 objc-runtime 交互有三種方式
- 通過 objc 源代碼 我們?cè)趯?objc 源代碼的時(shí)候,在編譯期間有的就已經(jīng)被翻譯優(yōu)化了.背后變成了調(diào)用運(yùn)行時(shí)的代碼
- 就是通過 nsobject 中的一些方法 nsobject 不規(guī)定類的具體行為.只定義了它們的基本必須結(jié)構(gòu)
- 最后的,自然就是通過runtime函數(shù)庫直接調(diào)用
how the message expressions are converted into objc_msgSend function calls, and how you can refer to methods by name. It then explains how you can take advantage of objc_msgSend, and how—if you need to—you can circumvent dynamic binding.
如果你對(duì)objc消息機(jī)制掌握的很好,完全可以繞過動(dòng)態(tài)綁定實(shí)現(xiàn)黑魔法
消息的最終接收者都是在運(yùn)行時(shí)才確定的
[receiver message]
編譯器轉(zhuǎn)換為:
objc_msgSend(receiver, selector) //兩個(gè)必須參數(shù)
objc_msgSend(receiver, selector, arg1, arg2, ...)//之后可選參數(shù)
The messaging function does everything necessary for dynamic binding:
It first finds the procedure (method implementation) that the selector refers to. Since the same method can be implemented differently by separate classes, the precise procedure that it finds depends on the class of the receiver.
It then calls the procedure, passing it the receiving object (a pointer to its data), along with any arguments that were specified for the method.
Finally, it passes on the return value of the procedure as its own return value.
作用:根據(jù)message 和 receiver的類的找到唯一的方法
我們先來看看類是什么 objc-class
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; //指向元類
#if !__OBJC2__
Class super_class //指向父類 OBJC2_UNAVAILABLE;
const char *name //類名 OBJC2_UNAVAILABLE;
long version //版本信息 OBJC2_UNAVAILABLE;
long info //類信息 供運(yùn)行時(shí)使用的標(biāo)識(shí)符 OBJC2_UNAVAILABLE;
long instance_size //實(shí)例大小 OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars //成員變量鏈表 OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists //方法鏈表 OBJC2_UNAVAILABLE;
struct objc_cache *cache //方法緩存 OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols..//協(xié)議鏈表 OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
typedef struct objc_class *Class;
這是需要了解的東西 一個(gè)實(shí)例中都有一個(gè) isa 指針,指向一個(gè) Class(objc_class*)的結(jié)構(gòu)體的指針,也就是實(shí)例指向自己的類,類中也有一個(gè) Class(isa)指向元類.一切皆對(duì)象中,類也是對(duì)象
![](https://developer.apple.com/library/prerelease/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Art/messaging1.gif)
可以看到很清楚顯示了實(shí)例和類之間的關(guān)系
When a message is sent to an object, the messaging function follows the object’s isa pointer to the class structure where it looks up the method selector in the dispatch table. If it can’t find the selector there, objc_msgSend follows the pointer to the superclass and tries to find the selector in its dispatch table. Successive failures cause objc_msgSend to climb the class hierarchy until it reaches the NSObject class. Once it locates the selector, the function calls the method entered in the table and passes it the receiving object’s data structure.
當(dāng)一個(gè)消息被發(fā)送到一個(gè)對(duì)象時(shí),函數(shù)調(diào)度機(jī)制根據(jù)這個(gè)對(duì)象的 isa 指針找到類的繼承結(jié)構(gòu)在類中的 dispatch_table 中找到這個(gè)方法,循環(huán)向上,找到之后,才開始進(jìn)行執(zhí)行 行話就是動(dòng)態(tài)綁定.....
為了加速動(dòng)態(tài)綁定的速度,每個(gè)類都有一個(gè)調(diào)度緩存,一些多次被使用的方法指針都在這里,在進(jìn)行調(diào)用表查詢之前,所有的方法都會(huì)先去查詢這個(gè) cache 中的方法.
isa:需要注意的是在Objective-C中梨树,所有的類自身也是一個(gè)對(duì)象诈悍,這個(gè)對(duì)象的Class里面也有一個(gè)isa指針黎侈,它指向metaClass(元類)淤年,
super_class:指向該類的父類彪薛,如果該類已經(jīng)是最頂層的根類(如NSObject或NSProxy),則super_class為NULL。
cache:用于緩存最近使用的方法。一個(gè)接收者對(duì)象接收到一個(gè)消息時(shí)佩厚,它會(huì)根據(jù)isa指針去查找能夠響應(yīng)這個(gè)消息的對(duì)象。在實(shí)際使用中说订,這個(gè)對(duì)象只有一部分方法是常用的抄瓦,很多方法其實(shí)很少用或者根本用不上。這種情況下陶冷,如果每次消息來時(shí)钙姊,我們都是methodLists中遍歷一遍,性能勢(shì)必很差埂伦。這時(shí)煞额,cache就派上用場(chǎng)了。在我們每次調(diào)用過一個(gè)方法后沾谜,這個(gè)方法就會(huì)被緩存到cache列表中膊毁,下次調(diào)用的時(shí)候runtime就會(huì)優(yōu)先去cache中查找,如果cache沒有类早,才去methodLists中查找方法媚媒。這樣嗜逻,對(duì)于那些經(jīng)常用到的方法的調(diào)用涩僻,但提高了調(diào)用的效率。就是緩存方法
現(xiàn)在看看對(duì)象的定義 (所有的都是經(jīng)過c包裝后的)
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY; //實(shí)例對(duì)象擁有指向類的指針
};
typedef struct objc_object *id;
當(dāng)創(chuàng)建一個(gè)特定類的實(shí)例對(duì)象時(shí),分配的內(nèi)存包含一個(gè)objc_object數(shù)據(jù)結(jié)構(gòu),NSObject類的alloc和allocWithZone:方法底層調(diào)用函數(shù)class_createInstance來創(chuàng)建objc_object數(shù)據(jù)結(jié)構(gòu).
常見的id逆日,它是一個(gè)objc_object結(jié)構(gòu)類型的指針嵌巷。 代表任何對(duì)象
meta-class是一個(gè)類對(duì)象的類。
當(dāng)我們向一個(gè)對(duì)象發(fā)送消息時(shí)室抽,runtime會(huì)在這個(gè)對(duì)象所屬的這個(gè)類的方法列表中查找方法搪哪;而向一個(gè)類發(fā)送消息時(shí),會(huì)在這個(gè)類的meta-class的方法列表中查找坪圾。
meta-class存儲(chǔ)著一個(gè)類的所有類方法晓折。每個(gè)類都會(huì)有一個(gè)單獨(dú)的meta-class.所有meta-class類的isa 指向的都是 NSObject的meta-class類..也就是當(dāng)我們調(diào)用類方法的時(shí)候,實(shí)際是去元類中查找.....
類與對(duì)象操作函數(shù)
runtime提供了大量的函數(shù)來操作類與對(duì)象。類的操作方法大部分是以class為前綴的兽泄,而對(duì)象的操作方法大部分是以objc或object_為前綴漓概。
When objc_msgSend finds the procedure that implements a method, it calls the procedure and passes it all the arguments in the message. It also passes the procedure two hidden arguments:
The receiving object
The selector for the method
These arguments give every method implementation explicit information about the two halves of the message expression that invoked it. They’re said to be “hidden” because they aren’t declared in the source code that defines the method. They’re inserted into the implementation when the code is compiled.
每底層objc_msgsend的方法在運(yùn)行時(shí)都會(huì)有兩個(gè)隱含的參數(shù) 在編譯時(shí)被自動(dòng)添加 一個(gè)是 self.指向receiver 一個(gè)是_cmd..代表的是方法本身
下面官方代碼測(cè)試這兩個(gè)參數(shù)
- strange
{
id target = getTheReceiver();
SEL method = getTheMethod();
if ( target == self || method == _cmd )
return nil;
return [target performSelector:method];
}
self 很有用啊,我們不是經(jīng)常通過 self 獲得消息和成員變量嘛
每次執(zhí)行方法的時(shí)候,都需要在調(diào)度表中查詢找到函數(shù)的入口地址,這對(duì)于一般方法沒問題.但是對(duì)于需要超高效率和執(zhí)行次數(shù)多的方法,我們可以直接跳過動(dòng)態(tài)綁定
sing methodForSelector: to circumvent dynamic binding saves most of the time required by messaging. However, the savings will be significant only where a particular message is repeated many times, as in the for loop shown above.
我們一般用 methodForSelector 來跳過動(dòng)態(tài)綁定這]法是得到 selector 的實(shí)際函數(shù)
| SEL1 | SEL2 | SEL3 |
| IMP1 | IMP2 | IMP3 |
sel 只是一個(gè)方法的標(biāo)志符,真正的執(zhí)行代碼地址是 imp 指向的地方 methodforselector可以的某個(gè)方法標(biāo)識(shí)符的地址,我們也可以在程序中之直接使用
IMP imp = [ NSObject methodForSelector:@selector(class)];
imp();
typedef id (*IMP)(id, SEL,... );
IMP is a C type referring to the implementation of a method, also known as an implementation pointer. It's a pointer to a function returning id, and with self and a method selector (available inside method definitions as the variable _cmd) as the first arguments
可以看到 id 是一種數(shù)據(jù)類型,imp 是一個(gè)指向返回?cái)?shù)據(jù)類型是 id 的函數(shù)的指針,這個(gè)函數(shù)的第一個(gè)參數(shù)和第二個(gè)參數(shù)是self 和這個(gè)函數(shù)名_cmd;
objc 將許多需要編譯和鏈接時(shí)確定的延遲到運(yùn)行時(shí)確定,這樣就發(fā)生了許多有趣的東西.比如.我們可以動(dòng)態(tài)的給類添加屬性使用 @dynamic propertyName;
動(dòng)態(tài)的告訴編譯器消息使用的屬性
我們也可以動(dòng)態(tài)的給類添加方法.(所有的這些和 java 中因?yàn)橛刑摂M機(jī)而有的反射有異曲同工之妙)
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
class_addMethod 接收的參數(shù):要添加的類 ,方法標(biāo)識(shí)符 sel;方法執(zhí)行入口地址;描述符
我們一定要有一個(gè)意識(shí)(runtime 是 一個(gè)函數(shù)庫 幫助我們更好的處理 objc 和 java 虛擬機(jī)做的事情是很類似的 這是動(dòng)態(tài)語言的一個(gè)大特性)
消息轉(zhuǎn)發(fā)
當(dāng)一個(gè)對(duì)象能接收一個(gè)消息時(shí),就會(huì)走正常的方法調(diào)用流程病梢。但如果一個(gè)對(duì)象無法接收指定消息時(shí)胃珍,又會(huì)發(fā)生什么事呢?默認(rèn)情況下蜓陌,如果是以 [object message]的方式調(diào)用方法觅彰,如果object無法響應(yīng)message消息時(shí),編譯器會(huì)報(bào)錯(cuò)钮热。但如果是以perform…的形式來調(diào)用填抬,則需要等到運(yùn) 行時(shí)才能確定object是否能接收message消息。如果不能隧期,則程序崩潰痴奏。
所以我們不能確定一個(gè)對(duì)象是否可以接受一個(gè)消息的時(shí)候,會(huì)先進(jìn)行判斷
if ([self respondsToSelector:@selector(method)]) {
[self performSelector:@selector(method)];
}
當(dāng)一個(gè)對(duì)象無法接收某一消息時(shí),就會(huì)啟動(dòng)所謂”消息轉(zhuǎn)發(fā)(message forwarding)“機(jī)制厌秒,通過這一機(jī)制读拆,我們可以告訴對(duì)象如何處理未知的消息。默認(rèn)情況下鸵闪,對(duì)象接收到未知的消息檐晕,會(huì)導(dǎo)致程序崩潰,報(bào)錯(cuò)
untime的強(qiáng)大之處在于它能在運(yùn)行時(shí)創(chuàng)建類和對(duì)象。
動(dòng)態(tài)創(chuàng)建類
動(dòng)態(tài)創(chuàng)建類涉及到以下幾個(gè)函數(shù):
// 創(chuàng)建一個(gè)新類和元類
Class objc_allocateClassPair ( Class superclass, const char *name, size_t extraBytes );
// 銷毀一個(gè)類及其相關(guān)聯(lián)的類
void objc_disposeClassPair ( Class cls );
// 在應(yīng)用中注冊(cè)由objc_allocateClassPair創(chuàng)建的類
void objc_registerClassPair ( Class cls );
和 java 中的反射有異曲同工之妙
息轉(zhuǎn)發(fā)機(jī)制基本上分為三個(gè)步驟:
動(dòng)態(tài)方法解析
備用接收者
完整轉(zhuǎn)發(fā)
下面我們?cè)敿?xì)討論一下這三個(gè)步驟蚌讼。
動(dòng)態(tài)方法解析
對(duì)象在接收到未知的消息時(shí)辟灰,首先會(huì)調(diào)用所屬類的類方法+resolveInstanceMethod:(實(shí)例方法)或 者+resolveClassMethod:(類方法)。在這個(gè)方法中篡石,我們有機(jī)會(huì)為該未知消息新增一個(gè)”處理方法”“芥喇。不過使用該方法的前提是我們已經(jīng) 實(shí)現(xiàn)了該”處理方法”,只需要在運(yùn)行時(shí)通過class_addMethod函數(shù)動(dòng)態(tài)添加到類里面就可以了凰萨。如下代碼所示:
void functionForMethod1(id self, SEL _cmd) {
NSLog(@"%@, %p", self, _cmd);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSString *selectorString = NSStringFromSelector(sel);
if ([selectorString isEqualToString:@"method1"]) {
class_addMethod(self.class, @selector(method1), (IMP)functionForMethod1, "@:");
}
return [super resolveInstanceMethod:sel];
}
不過這種方案更多的是為了實(shí)現(xiàn)@dynamic屬性继控。
備用接收者
如果在上一步無法處理消息械馆,則Runtime會(huì)繼續(xù)調(diào)以下方法:
- (id)forwardingTargetForSelector:(SEL)aSelector
如果一個(gè)對(duì)象實(shí)現(xiàn)了這個(gè)方法,并返回一個(gè)非nil的結(jié)果武通,則這個(gè)對(duì)象會(huì)作為消息的新接收者霹崎,且消息會(huì)被分發(fā)到這個(gè)對(duì)象。當(dāng)然這個(gè)對(duì)象不能是self自身冶忱,否則就是出現(xiàn)無限循環(huán)尾菇。當(dāng)然,如果我們沒有指定相應(yīng)的對(duì)象來處理aSelector囚枪,則應(yīng)該調(diào)用父類的實(shí)現(xiàn)來返回結(jié)果派诬。
使用這個(gè)方法通常是在對(duì)象內(nèi)部,可能還有一系列其它對(duì)象能處理該消息链沼,我們便可借這些對(duì)象來處理消息并返回千埃,這樣在對(duì)象外部看來,還是由該對(duì)象親自處理了這一消息忆植。如下代碼所示:
@interface SUTRuntimeMethodHelper : NSObject
- (void)method2;
@end
@implementation SUTRuntimeMethodHelper
- (void)method2 {
NSLog(@"%@, %p", self, _cmd);
}
@end
#pragma mark -
@interface SUTRuntimeMethod () {
SUTRuntimeMethodHelper *_helper;
}
@end
@implementation SUTRuntimeMethod
+ (instancetype)object {
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (self != nil) {
_helper = [[SUTRuntimeMethodHelper alloc] init];
}
return self;
}
- (void)test {
[self performSelector:@selector(method2)];
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"forwardingTargetForSelector");
NSString *selectorString = NSStringFromSelector(aSelector);
// 將消息轉(zhuǎn)發(fā)給_helper來處理
if ([selectorString isEqualToString:@"method2"]) {
return _helper;
}
return [super forwardingTargetForSelector:aSelector];
}
@end
這一步合適于我們只想將消息轉(zhuǎn)發(fā)到另一個(gè)能處理該消息的對(duì)象上放可。但這一步無法對(duì)消息進(jìn)行處理,如操作消息的參數(shù)和返回值朝刊。
完整消息轉(zhuǎn)發(fā)
如果在上一步還不能處理未知消息耀里,則唯一能做的就是啟用完整的消息轉(zhuǎn)發(fā)機(jī)制了。此時(shí)會(huì)調(diào)用以下方法:
- (void)forwardInvocation:(NSInvocation *)anInvocation
運(yùn)行時(shí)系統(tǒng)會(huì)在這一步給消息接收者最后一次機(jī)會(huì)將消息轉(zhuǎn)發(fā)給其它對(duì)象拾氓。對(duì)象會(huì)創(chuàng)建一個(gè)表示消息的NSInvocation對(duì)象冯挎,把與尚未處理的消息 有關(guān)的全部細(xì)節(jié)都封裝在anInvocation中,包括selector咙鞍,目標(biāo)(target)和參數(shù)房官。我們可以在forwardInvocation 方法中選擇將消息轉(zhuǎn)發(fā)給其它對(duì)象。
forwardInvocation:方法的實(shí)現(xiàn)有兩個(gè)任務(wù):
定位可以響應(yīng)封裝在anInvocation中的消息的對(duì)象续滋。這個(gè)對(duì)象不需要能處理所有未知消息翰守。
使用anInvocation作為參數(shù),將消息發(fā)送到選中的對(duì)象疲酌。anInvocation將會(huì)保留調(diào)用結(jié)果蜡峰,運(yùn)行時(shí)系統(tǒng)會(huì)提取這一結(jié)果并將其發(fā)送到消息的原始發(fā)送者。
不過朗恳,在這個(gè)方法中我們可以實(shí)現(xiàn)一些更復(fù)雜的功能湿颅,我們可以對(duì)消息的內(nèi)容進(jìn)行修改,比如追回一個(gè)參數(shù)等粥诫,然后再去觸發(fā)消息油航。另外,若發(fā)現(xiàn)某個(gè)消息不應(yīng)由本類處理怀浆,則應(yīng)調(diào)用父類的同名方法谊囚,以便繼承體系中的每個(gè)類都有機(jī)會(huì)處理此調(diào)用請(qǐng)求怕享。
還有一個(gè)很重要的問題,我們必須重寫以下方法:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
消息轉(zhuǎn)發(fā)機(jī)制使用從這個(gè)方法中獲取的信息來創(chuàng)建NSInvocation對(duì)象秒啦。因此我們必須重寫這個(gè)方法,為給定的selector提供一個(gè)合適的方法簽名搀玖。
完整的示例如下所示:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if (!signature) {
if ([SUTRuntimeMethodHelper instancesRespondToSelector:aSelector]) {
signature = [SUTRuntimeMethodHelper instanceMethodSignatureForSelector:aSelector];
}
}
return signature;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([SUTRuntimeMethodHelper instancesRespondToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:_helper];
}
}
NSObject的forwardInvocation:方法實(shí)現(xiàn)只是簡單調(diào)用了doesNotRecognizeSelector:方法余境,它不會(huì)轉(zhuǎn)發(fā)任何消息。這樣灌诅,如果不在以上所述的三個(gè)步驟中處理未知消息芳来,則會(huì)引發(fā)一個(gè)異常。
從某種意義上來講猜拾,forwardInvocation:就像一個(gè)未知消息的分發(fā)中心即舌,將這些未知的消息轉(zhuǎn)發(fā)給其它對(duì)象】嫱啵或者也可以像一個(gè)運(yùn)輸站一樣將所有未知消息都發(fā)送給同一個(gè)接收對(duì)象顽聂。這取決于具體的實(shí)現(xiàn)。
消息轉(zhuǎn)發(fā)與多重繼承
回過頭來看第二和第三步盯仪,通過這兩個(gè)方法我們可以允許一個(gè)對(duì)象與其它對(duì)象建立關(guān)系紊搪,以處理某些未知消息,而表面上看仍然是該對(duì)象在處理消息全景。通過這 種關(guān)系耀石,我們可以模擬“多重繼承”的某些特性,讓對(duì)象可以“繼承”其它對(duì)象的特性來處理一些事情爸黄。不過滞伟,這兩者間有一個(gè)重要的區(qū)別:多重繼承將不同的功能 集成到一個(gè)對(duì)象中,它會(huì)讓對(duì)象變得過大炕贵,涉及的東西過多梆奈;而消息轉(zhuǎn)發(fā)將功能分解到獨(dú)立的小的對(duì)象中,并通過某種方式將這些對(duì)象連接起來称开,并做相應(yīng)的消息轉(zhuǎn) 發(fā)鉴裹。
不過消息轉(zhuǎn)發(fā)雖然類似于繼承,但NSObject的一些方法還是能區(qū)分兩者钥弯。如respondsToSelector:和isKindOfClass:只能用于繼承體系径荔,而不能用于轉(zhuǎn)發(fā)鏈。便如果我們想讓這種消息轉(zhuǎn)發(fā)看起來像是繼承脆霎,則可以重寫這些方法总处,如以下代碼所示:
- (BOOL)respondsToSelector:(SEL)aSelector {
if ( [super respondsToSelector:aSelector] )
return YES;
else {
/* Here, test whether the aSelector message can
*
* be forwarded to another object and whether that
*
* object can respond to it. Return YES if it can.
*/
}
return NO;
}
這些東西我們只要了解就好,在開發(fā)中用到的不多
順便在這里說一下 objc中前面文章提到的數(shù)據(jù)類型
typedef struct objc_category *Category;
struct objc_category {
char *category_name OBJC2_UNAVAILABLE; // 分類名
char *class_name OBJC2_UNAVAILABLE; // 分類所 屬的類名
struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; // 實(shí)例方法列表
struct objc_method_list *class_methods OBJC2_UNAVAILABLE; // 類方法列表
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 分類所實(shí)現(xiàn)的協(xié)議列表
}
這是分類的結(jié)構(gòu)體 和類是不是很相似啊?哈哈
我們?cè)賮砜纯磪f(xié)議的struct
typedef struct objc_object Protocol;
簡單明了 大家一看,這不就是對(duì)象嘛
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY; //實(shí)例對(duì)象擁有指向類的指針
};
class isa 指向了實(shí)現(xiàn)協(xié)議的類..(自然也將協(xié)議中的方法在實(shí)現(xiàn)的類中可以找到)
runtime.h 提供了很多接口讓開發(fā)者靈活使用,有對(duì)類的,對(duì)對(duì)象的,對(duì)屬性,方法的.(分類的東西已經(jīng)包含在類中),對(duì)協(xié)議的
我們可以在程序中動(dòng)態(tài)產(chǎn)生 繼承 改變 注冊(cè) 獲得 銷毀 各個(gè)我門想要操作
最有趣又著名的莫過于sclector swizzing
這就是非常有用的的hook....
下一篇再講