OC是消息型語言,OC中的方法調(diào)用實際上是消息的發(fā)送,編譯器并不能決定程序真正執(zhí)行的到底是哪段代碼,這個工作,需要運行時系統(tǒng)來完成
消息發(fā)送
舉個例子:alloc init初始化方法 我們玩玩用消息發(fā)送的方式
使用之前記得配置一下,官方是不推薦我們直接使用這種的,應該說是不讓我們直接這樣用。這里只是玩玩,實際開發(fā)中不要這樣弄了
配置好后,導入頭文件 <objc/message.h>

//使用消息發(fā)送的形式 進行對象的創(chuàng)建與初始化 方法調(diào)用
penson *p = objc_msgSend([penson class], @selector(alloc));
p = objc_msgSend(p, @selector(init));
objc_msgSend(p, @selector(run));
//試試自己創(chuàng)建一個類 然后run NSLog(@"跑");
正題:消息轉(zhuǎn)發(fā)
[penson run]: unrecognized selector sent to instance 0x604000001c70
熟不熟悉,對象不能響應方法調(diào)用,大致就是這樣的。說白了,就是調(diào)用的方法未找到實現(xiàn)
當對象收到消息后,會在自己的方法列表中查找是否有該方法。如果沒有,那么找父類,一直找到NSObject。 如果都沒有那么就開始那個消息轉(zhuǎn)發(fā)三部曲了。
簡單點:當一個對象收到消息,會先在自己的繼承體系中尋找實現(xiàn),如果處理不了。那么就進行三步走
第一步,是否動態(tài)的添加方法
這一步是給我們的第一次挽救機會,我們可以在這里動態(tài)的添加一個方法。如果我們在這里處理了,那么下面的兩步不會走到
//.m 中
//首先認識兩個方法
//+ (BOOL)resolveInstanceMethod:(SEL)sel;//調(diào)用的是對象方法
//+ (BOOL)resolveClassMethod:(SEL)sel;//響應的是類方法
+ (BOOL)resolveInstanceMethod:(SEL)sel {
//加個判斷
if (sel == @selector(run)) {
class_addMethod(self, sel, (IMP)imp, "v@:");
}
return [super resolveInstanceMethod:sel];
//v@:
//第一個參數(shù)代表返回值: v代表無返回值()
//第二個參數(shù)與第三個參數(shù)是固定的模式:@代表self :代表_cmd;
//如果有參數(shù)可以加上第四個
}
+ (BOOL)resolveClassMethod:(SEL)sel {
class_addMethod(objc_getMetaClass(object_getClassName(self)), sel, (IMP)imp, "");
return [super resolveClassMethod:sel];
}
void imp(id self, SEL _cmd) {
//id self SEL _cmd 兩個隱藏的參數(shù) 一定會有的
NSLog(@"沒有找到相應方法");
}
第二步,是否將消息轉(zhuǎn)發(fā)給其它對象(備援接受者)
第二次挽救機會,如果在動態(tài)添加方法那里并沒有做什么處理。那么就會走到這一步。給你第二次挽救的機會
//.m 中
- (id)forwardingTargetForSelector:(SEL)aSelector {
//這里就是讓你把消息轉(zhuǎn)發(fā)給別人 讓別人響應去
//如果別人也沒有實現(xiàn) 那么還是會報錯
//這里的返回值如果是nil 或者 自身 表示轉(zhuǎn)發(fā)給自己或者不轉(zhuǎn)發(fā) 沒什么意義了 最后肯定會報錯
return nil;
}
第三步,完整的消息轉(zhuǎn)發(fā)
如果在前兩步都不作為,那么就到這最后一步了。最后一次挽救的機會。
這一步中,會創(chuàng)建NSInvocation對象,把與尚未處理的那條消息有關的全部細節(jié)都封于其中。此對象包含選擇子、目標及參數(shù)。在觸發(fā)NSInvocation對象時,“消息派發(fā)系統(tǒng)”把消息指派給目標對象。讓目標對象進行處理
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
//返回一個方法簽名
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
//消息派發(fā)給其它目標對象 而且可以派發(fā)給多個對象
//派發(fā)之前可以進行判斷一下
SEL selector =[anInvocation selector];
Ppenson *p1=[Ppenson new];
PPpenson *p2=[PPpenson new];
if ([p1 respondsToSelector:selector]) {
[anInvocation invokeWithTarget:p1];
}
if ([p2 respondsToSelector:selector]) {
[anInvocation invokeWithTarget:p2];
}
}
題外話 alloc init
alloc:給對象開辟一片內(nèi)存空間
init:對這片空間進行初始化
.有說alloc出來的并不是一個真正的NSObject對象 所以不能直接使用
.那么不知道有沒有試過直接alloc 并不進行init 你會發(fā)現(xiàn)依然能夠調(diào)用到對象中的方法
.既然調(diào)用了方法 難道不算是使用了這個對象 這一點我很疑惑
.關于這一點疑惑,看看init的作用 或許明白一丟丟
.init是對這片空間的初始化 如果不對這片空間進行初始化 那么這片空間是否還會存有數(shù)據(jù)。
.關于是否存有數(shù)據(jù)這一點 不能確定。只能說當某個對象釋放的時候,他之前占用的內(nèi)存空間并不會把數(shù)據(jù)給清理掉,只是告訴系統(tǒng)這塊空間可以分配出去了。
.那么也就是說 如果不進行init操作 我們雖然能夠進行訪問 但是是不安全的 可能訪問到了其它莫名的數(shù)據(jù)
.所以為了安全考慮 推薦使用init初始化一下
知識鏈接:
https://www.csdn.net/article/2015-07-06/2825133-objective-c-runtime/1 <runtime知識 很是詳盡>
http://www.cocoachina.com/ios/20150818/13075.html <方法緩存>