最近開(kāi)始在上海實(shí)習(xí),在公司負(fù)責(zé)研究JSPatch熱修復(fù)的相關(guān)事務(wù),其中需要好好學(xué)習(xí)一下Runtime宿百,這一塊是OC里面比較晦澀的東西轮纫,現(xiàn)將學(xué)習(xí)心得記錄下來(lái)腔寡。
邊寫(xiě)邊學(xué)
有兩個(gè)類(lèi):ViewController
和NewViewController
,分別有一個(gè)打印方法:test
和newTest
掌唾。
- (void)test
{
NSString *cls = NSStringFromClass([self class]);
NSString *sle = NSStringFromSelector(_cmd);
NSLog(@"origin methods: <%@> [%@]", cls, sle);
}
- (void)newTest
{
NSString *cls = NSStringFromClass([self class]);
NSString *sle = NSStringFromSelector(_cmd);
NSLog(@"now replaced method: <%@> [%@]", cls, sle);
}
下面可以在AppDelegate
中的application:didFinishLaunchingWithOptions:
執(zhí)行以下代碼放前,查看輸出結(jié)果,理解Runtime中動(dòng)態(tài)消息轉(zhuǎn)發(fā)的細(xì)節(jié)糯彬。
代碼各部分的執(zhí)行說(shuō)明可以見(jiàn)注釋凭语。
// 原本要執(zhí)行的實(shí)例中的方法
Class oriCls = NSClassFromString(@"ViewController");
id viewController = [[oriCls alloc] init];
SEL oriSel = NSSelectorFromString(@"test");
[viewController performSelector:oriSel];
Method oriMethod = class_getInstanceMethod(oriCls, oriSel);
IMP originIMP = method_getImplementation(oriMethod);
const char *oriTypes = method_getTypeEncoding(oriMethod);
/*
1、performSelector是運(yùn)行時(shí)系統(tǒng)負(fù)責(zé)去找方法的撩扒,在編譯時(shí)候不做任何校驗(yàn)似扔;如果直接調(diào)用編譯是會(huì)自動(dòng)校驗(yàn)。如果imageDownloader:didFinishWithImage:image:不存在搓谆,那么直接調(diào)用 在編譯時(shí)候就能夠發(fā)現(xiàn)(借助Xcode可以寫(xiě)完就發(fā)現(xiàn))炒辉,但是使用performSelector的話一定是在運(yùn)行時(shí)候才能發(fā)現(xiàn)(此時(shí)程序崩潰);Cocoa支持在運(yùn)行時(shí)向某個(gè)類(lèi)添加方法泉手,即方法編譯時(shí)不存在黔寇,但是運(yùn)行時(shí)候存在,這時(shí)候必然需要使用performSelector去調(diào)用斩萌。所以有時(shí)候如果使用了performSelector缝裤,為了程序的健壯性,會(huì)使用檢查方法
- (BOOL)respondsToSelector:(SEL)aSelector;
2颊郎、直接調(diào)用方法時(shí)候憋飞,一定要在頭文件中聲明該方法的使用,也要將頭文件import進(jìn)來(lái)姆吭。而使用performSelector時(shí)候榛做, 可以不用import頭文件包含方法的對(duì)象,直接用performSelector調(diào)用即可猾编。
*/
// 現(xiàn)在替換IMP指針到NewViewController中的newTest中
Class newCls = NSClassFromString(@"NewViewController");
SEL newSle = NSSelectorFromString(@"newTest");
Method newMethod = class_getInstanceMethod(newCls, newSle);
//IMP replaceIMP = class_getMethodImplementation(newCls, newSle);
IMP replaceIMP = method_getImplementation(newMethod);
const char *newTypes = method_getTypeEncoding(newMethod);
// 然后繼續(xù)執(zhí)行原方法瘤睹,原方法的Class和SEL仍舊是ViewController和test,但是動(dòng)態(tài)執(zhí)行時(shí)被替換成了NewViewController和newTest
class_replaceMethod(oriCls, oriSel, replaceIMP, newTypes);
// 新增一個(gè)oriTest方法答倡,指向原來(lái)的test實(shí)現(xiàn)
class_addMethod(oriCls,@selector(oriTest), originIMP, oriTypes);
[viewController performSelector:oriSel];
[viewController performSelector:@selector(oriTest)];
執(zhí)行結(jié)果如下圖:
執(zhí)行結(jié)果
從結(jié)果中可以清楚地看到方法的替換和保存轰传。
完善內(nèi)容,待更~
如果這篇文章對(duì)您有幫助瘪撇,歡迎點(diǎn)贊和轉(zhuǎn)發(fā)获茬。有任何問(wèn)題或者建議港庄,也歡迎留言!