OC是一門動態(tài)語言,方法的調(diào)用本質(zhì)上是利用objc_msgSend進行"發(fā)消息",也就是某類或某對象調(diào)用其某方法,本質(zhì)上是向某個對象的指針發(fā)送了一條消息,在此之前方法和對象(或類)都沒有真正確定下來即動態(tài)綁定,消息與方法的真正實現(xiàn)是在執(zhí)行階段綁定的翩活,而非編譯階段.
所謂動態(tài)綁定,我舉一個簡單的C語言例子
#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;
}
對于上面這一類型到底是調(diào)用hello函數(shù)還是goodbye函數(shù),這兩個函數(shù)都是已經(jīng)確定的,就像是有一本語文書一本數(shù)學(xué)書放在你面前,而你已經(jīng)知道這兩本中的一本最終會被你拿到手上,語文書和數(shù)學(xué)書已經(jī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;
}
而對于這一種類型,我們可以看到我們聲明了一個函數(shù)指針func,就好比你拿到了夠買其中一本書的錢,只是錢在你手里,到底是買語文書還是數(shù)學(xué)書還沒有確定,手上的錢是會動態(tài)改變的,這叫做動態(tài)綁定.
當向一個對象發(fā)送消息時,objc_msgSend方法根據(jù)對象的isa指針找到對象的類凰荚,然后在類的調(diào)度表(dispatch table)中查找selector。如果無法找到selector吏口,objc_msgSend通過指向父類的指針找到父類术瓮,并在父類的調(diào)度表(dispatch table)中查找selector蝠咆,以此類推直到NSObject類踊东。一旦查找到selector,objc_msgSend方法根據(jù)調(diào)度表的內(nèi)存地址調(diào)用該實現(xiàn)刚操。 通過這種方式闸翅,message與方法的真正實現(xiàn)在執(zhí)行階段才綁定。
為了保證消息發(fā)送與執(zhí)行的效率菊霜,系統(tǒng)會將全部selector和使用過的方法的內(nèi)存地址緩存起來坚冀。每個類都有一個獨立的緩存,緩存包含有當前類自己的 selector以及繼承自父類的selector鉴逞。查找調(diào)度表(dispatch table)前记某,消息發(fā)送系統(tǒng)首先檢查receiver對象的緩存。
我們現(xiàn)在看一下objc_msgSend如何使用
先在控制器中定義一個方法
- (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 ++;
}
然后對其進行調(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];
// 這里到底有幾個參數(shù)你就放幾個id,當然你也可以直接指定類型
((void(*)(id,SEL, id,id,id,id))objc_msgSend)(self,change , colorOne, colorTwo,colorThree,colorFour);
}
交換兩個方法
在實際開發(fā)中我們會遇到這樣一個問題,當項目開發(fā)得差不多的時候,或者說到了項目迭代的時候,我們發(fā)現(xiàn)了內(nèi)存泄露(如block的不規(guī)范使用導(dǎo)致),不知道到底是哪個View或者說是哪個控制器沒有正常被回收.那么我們常用的做法就是在- (void)dealloc方法中打印某些字樣,去控制臺看到底是在哪些界面跳轉(zhuǎn)或者回跳的時候哪些對象沒有調(diào)用dealloc方法.
在這個時候我們要是去一個個文件中重寫dealloc方法就太繁瑣了,而且容易漏掉一些類.面對這種情景,使用分類,在分類中交換方法是最好的解決辦法,而且它的實現(xiàn)不需要引入分類頭文件
#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方法的時候會調(diào)用該方法
- (void)myDealloc{
NSLog(@"%@掛了", self);
//此刻實際是在調(diào)用dealloc方法
[self myDealloc];
}
我這里寫了一個UIView的分類,也就是說所有UIView的子類被回收的時候都能夠調(diào)用這個犯法,其中+ (void)load 方法會被調(diào)用一次,它并不需要該文件被使用才會被調(diào)用,也就是在能內(nèi)存中加載的時候就會被調(diào)用,且僅有一次,在這一次調(diào)用中我們把UIView的兩個對象方法進行了替換(也就是在最早的時候就交換了方法,相當于把這兩條神經(jīng)給交換接上了).
以上就是一個經(jīng)常遇到的的runtime替換兩個方法的使用場景,若有寫的不好的地方歡迎指出.
版權(quán)聲明:本文版權(quán)歸本文作者所有构捡,始發(fā)于簡書液南,如需轉(zhuǎn)載請聯(lián)系作者,違者必究.