OC是一門(mén)動(dòng)態(tài)語(yǔ)言,方法的調(diào)用本質(zhì)上是利用objc_msgSend進(jìn)行"發(fā)消息",也就是某類(lèi)或某對(duì)象調(diào)用其某方法,本質(zhì)上是向某個(gè)對(duì)象的指針發(fā)送了一條消息,在此之前方法和對(duì)象(或類(lèi))都沒(méi)有真正確定下來(lái)即動(dòng)態(tài)綁定,消息與方法的真正實(shí)現(xiàn)是在執(zhí)行階段綁定的,而非編譯階段.
所謂動(dòng)態(tài)綁定,我舉一個(gè)簡(jiǎn)單的C語(yǔ)言例子
#import <stdio.h>
void Chinese() {
printf("Chinese book");
}
void Math() {
printf("Math book");
}
void doTheThing(int type) {
if (type == 0) {
Chinese();
} else {
Math();
}
return 0;
}
對(duì)于上面這一類(lèi)型到底是調(diào)用hello函數(shù)還是goodbye函數(shù),這兩個(gè)函數(shù)都是已經(jīng)確定的,就像是有一本語(yǔ)文書(shū)一本數(shù)學(xué)書(shū)放在你面前,而你已經(jīng)知道這兩本中的一本最終會(huì)被你拿到手上,語(yǔ)文書(shū)和數(shù)學(xué)書(shū)已經(jīng)是放在那里的了,不是動(dòng)態(tài)改變啊的.
#import <stdio.h>
void Chinese() {
printf("Chinese book");
}
void Math() {
printf("Math book");
}
void doTheThing(int type) {
void (*func)();
if (type == 0) {
func = Chinese;
} else {
func = Math;
}
func();
return 0;
}
而對(duì)于這一種類(lèi)型,我們可以看到我們聲明了一個(gè)函數(shù)指針func,就好比你拿到了夠買(mǎi)其中一本書(shū)的錢(qián),只是錢(qián)在你手里,到底是買(mǎi)語(yǔ)文書(shū)還是數(shù)學(xué)書(shū)還沒(méi)有確定,手上的錢(qián)是會(huì)動(dòng)態(tài)改變的,這叫做動(dòng)態(tài)綁定.
當(dāng)向一個(gè)對(duì)象發(fā)送消息時(shí),objc_msgSend方法根據(jù)對(duì)象的isa指針找到對(duì)象的類(lèi),然后在類(lèi)的調(diào)度表(dispatch table)中查找selector。如果無(wú)法找到selector,objc_msgSend通過(guò)指向父類(lèi)的指針找到父類(lèi),并在父類(lèi)的調(diào)度表(dispatch table)中查找selector,以此類(lèi)推直到NSObject類(lèi)。一旦查找到selector,objc_msgSend方法根據(jù)調(diào)度表的內(nèi)存地址調(diào)用該實(shí)現(xiàn)。 通過(guò)這種方式,message與方法的真正實(shí)現(xiàn)在執(zhí)行階段才綁定。
為了保證消息發(fā)送與執(zhí)行的效率,系統(tǒng)會(huì)將全部selector和使用過(guò)的方法的內(nèi)存地址緩存起來(lái)。每個(gè)類(lèi)都有一個(gè)獨(dú)立的緩存,緩存包含有當(dāng)前類(lèi)自己的 selector以及繼承自父類(lèi)的selector。查找調(diào)度表(dispatch table)前,消息發(fā)送系統(tǒng)首先檢查receiver對(duì)象的緩存。
我們現(xiàn)在看一下objc_msgSend如何使用
先在控制器中定義一個(gè)方法
- (void)changeColor:(UIColor *)colorOne colorTwo:(UIColor *)colorTwo colorThree:(UIColor *)colorThree colorFour:(UIColor *)colorFour{
static NSUInteger count = 0;
NSUInteger k = count %4 ;
switch (k) {
case 0:
self.view.backgroundColor = colorOne;
break;
case 1:
self.view.backgroundColor = colorTwo;
break;
case 2:
self.view.backgroundColor = colorThree;
break;
case 3:
self.view.backgroundColor = colorFour;
break;
default:
break;
}
count ++;
}
然后對(duì)其進(jìn)行調(diào)用
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
SEL change = @selector(changeColor:colorTwo:colorThree:colorFour:);
UIColor *colorOne = [UIColor blueColor];
UIColor *colorTwo = [UIColor greenColor];
UIColor *colorThree = [UIColor redColor];
UIColor *colorFour = [UIColor yellowColor];
// 這里到底有幾個(gè)參數(shù)你就放幾個(gè)id,當(dāng)然你也可以直接指定類(lèi)型
((void(*)(id,SEL, id,id,id,id))objc_msgSend)(self,change , colorOne, colorTwo,colorThree,colorFour);
}
交換兩個(gè)方法
在實(shí)際開(kāi)發(fā)中我們會(huì)遇到這樣一個(gè)問(wèn)題,當(dāng)項(xiàng)目開(kāi)發(fā)得差不多的時(shí)候,或者說(shuō)到了項(xiàng)目迭代的時(shí)候,我們發(fā)現(xiàn)了內(nèi)存泄露(如block的不規(guī)范使用導(dǎo)致),不知道到底是哪個(gè)View或者說(shuō)是哪個(gè)控制器沒(méi)有正常被回收.那么我們常用的做法就是在- (void)dealloc方法中打印某些字樣,去控制臺(tái)看到底是在哪些界面跳轉(zhuǎn)或者回跳的時(shí)候哪些對(duì)象沒(méi)有調(diào)用dealloc方法.
在這個(gè)時(shí)候我們要是去一個(gè)個(gè)文件中重寫(xiě)dealloc方法就太繁瑣了,而且容易漏掉一些類(lèi).面對(duì)這種情景,使用分類(lèi),在分類(lèi)中交換方法是最好的解決辦法,而且它的實(shí)現(xiàn)不需要引入分類(lèi)頭文件
#import "UIView+dealloc.h"
#import <objc/runtime.h>
@implementation UIView (dealloc)
+ (void)load{
Method m2 = class_getInstanceMethod([self class], @selector(myDealloc));
Method m1 = class_getInstanceMethod([self class], NSSelectorFromString(@"dealloc"));
method_exchangeImplementations(m2, m1);
}
//系統(tǒng)調(diào)用dealloc方法的時(shí)候會(huì)調(diào)用該方法
- (void)myDealloc{
NSLog(@"%@掛了", self);
//此刻實(shí)際是在調(diào)用dealloc方法
[self myDealloc];
}
我這里寫(xiě)了一個(gè)UIView的分類(lèi),也就是說(shuō)所有UIView的子類(lèi)被回收的時(shí)候都能夠調(diào)用這個(gè)犯法,其中+ (void)load 方法會(huì)被調(diào)用一次,它并不需要該文件被使用才會(huì)被調(diào)用,也就是在能內(nèi)存中加載的時(shí)候就會(huì)被調(diào)用,且僅有一次,在這一次調(diào)用中我們把UIView的兩個(gè)對(duì)象方法進(jìn)行了替換(也就是在最早的時(shí)候就交換了方法,相當(dāng)于把這兩條神經(jīng)給交換接上了).
以上就是一個(gè)經(jīng)常遇到的的runtime替換兩個(gè)方法的使用場(chǎng)景,若有寫(xiě)的不好的地方歡迎指出.
版權(quán)聲明:本文版權(quán)歸本文作者所有,始發(fā)于簡(jiǎn)書(shū),如需轉(zhuǎn)載請(qǐng)聯(lián)系作者,違者必究.