作為iOS開(kāi)發(fā)者,對(duì)runtime
應(yīng)該都有耳聞覆劈,這是Objective-C
這門(mén)開(kāi)發(fā)語(yǔ)言的動(dòng)態(tài)性最好的體現(xiàn)保礼,利用runtime
可以做很多事沛励,極大地提高開(kāi)發(fā)效率。最近一直在研究runtime
的黑魔法Method Swizzling
炮障,也踩了一些坑目派,在這里分享一下心得,如有錯(cuò)誤或不當(dāng)?shù)牡胤叫灿凑?qǐng)各位看官指正企蹭,小生定感激不盡。
Method Swizzling
中最重要的兩個(gè)方法就是交換實(shí)例方法和交換類方法智末,代碼如下:
/**
交換實(shí)例方法
@param cls 類對(duì)象
@param originalSel 原始方法
@param swizzlingSel 替換方法
*/
+ (void)ht_swizzleInstanceMethodForClass:(Class)cls
originalSelector:(SEL)originalSel
swizzlingSelector:(SEL)swizzlingSel {
Method originalMethod = class_getInstanceMethod(cls, originalSel);
Method swizzlingMethod = class_getInstanceMethod(cls, swizzlingSel);
BOOL addedMethod = class_addMethod(cls,
originalSel,
method_getImplementation(swizzlingMethod),
method_getTypeEncoding(swizzlingMethod));
if (addedMethod) {
class_replaceMethod(cls,
swizzlingSel,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzlingMethod);
}
}
/**
交換類方法
@param cls 元類對(duì)象
@param originalSel 原始方法
@param swizzlingSel 替換方法
*/
+ (void)ht_swizzleClassMethodForClass:(Class)cls
originalSelector:(SEL)originalSel
swizzlingSelector:(SEL)swizzlingSel {
Method originalMethod = class_getClassMethod(cls, originalSel);
Method swizzlingMethod = class_getClassMethod(cls, swizzlingSel);
BOOL didAddMethod = class_addMethod(cls,
originalSel,
method_getImplementation(swizzlingMethod),
method_getTypeEncoding(swizzlingMethod));
if (didAddMethod) {
class_replaceMethod(cls,
swizzlingSel,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzlingMethod);
}
}
既然是交換方法谅摄,首先就得拿到原始方法和交換方法的Method
,然后給當(dāng)前類class_addMethod
要交換的方法系馆,如果didAddMethod
則class_replaceMethod
原始方法實(shí)現(xiàn)送漠,否則method_exchangeImplementations
兩個(gè)方法實(shí)現(xiàn)。這里需要注意一下由蘑,就是在交換方法里面闽寡,我們看到會(huì)有一個(gè)class_addMethod
方法,返回值是BOOL
類型纵穿,這個(gè)方法是給當(dāng)前類添加方法下隧,然后根據(jù)返回結(jié)果來(lái)判斷,如果添加成功谓媒,則替換方法實(shí)現(xiàn)淆院,否則交換兩個(gè)方法實(shí)現(xiàn)。
這里有個(gè)疑惑困擾我句惯,通過(guò)代碼發(fā)現(xiàn)土辩,這個(gè)方法的返回值一直是NO
,也就是說(shuō)不需要這個(gè)判斷也可以實(shí)現(xiàn)方法交換抢野,那為什么需要多此一舉呢拷淘?上古名人說(shuō)過(guò)一句話:存在即道理,于是我在網(wǎng)絡(luò)的海洋中暢游了一番指孤,經(jīng)過(guò)不懈努力启涯,終于解開(kāi)疑惑(老臉一紅ing)。有個(gè)解釋說(shuō)是為了防止父類調(diào)用子類方法恃轩,剛開(kāi)始一臉懵逼结洼,父類怎么調(diào)用子類方法呢?原來(lái)在class_getInstanceMethod
獲取原始方法的時(shí)候叉跛,有可能當(dāng)前類并沒(méi)有該方法松忍,沿著繼承鏈找到了父類中的方法,此刻將要交換的是父類方法和子類方法筷厘,而恰好子類方法中調(diào)用了子類的其他方法鸣峭,這個(gè)時(shí)候父類調(diào)用原來(lái)的方法,因?yàn)榻粨Q實(shí)現(xiàn)的緣故摊溶,實(shí)際上調(diào)用了子類的方法實(shí)現(xiàn),父類肯定不可以調(diào)用子類方法更扁,從而導(dǎo)致崩潰盖腕,所以要做一層保護(hù)浓镜,如果當(dāng)前類沒(méi)有方法,則先加上膛薛,再進(jìn)行交換。
關(guān)于Method Swizzling
中的兩個(gè)方法和注意點(diǎn)到這里就介紹的差不多了哄啄,但是還沒(méi)有感受到Method Swizzling
帶來(lái)的魅力雅任,下面我就要開(kāi)始正經(jīng)的胡說(shuō)八道了咨跌,前方高能??
既然runtime
中提供了交換方法,那么我們就可以hook
一些系統(tǒng)的方法锌半,做一些額外的處理禽车,舉個(gè)??,埋點(diǎn)是我們移動(dòng)開(kāi)發(fā)中常用的一種分析用戶行為的手段刊殉,尤其是頁(yè)面停留時(shí)間殉摔,能分析出用戶的喜好。如果直接在每個(gè)頁(yè)面的viewDidAppear
和viewDidDisappear
中添加統(tǒng)計(jì)方法记焊,很費(fèi)時(shí)間和精力逸月,如果利用Method Swizzling
就可以直接在一個(gè)方法中實(shí)現(xiàn),效率杠杠的遍膜。但是我今天要說(shuō)的不是統(tǒng)計(jì)的事碗硬,而是令我們比較頭疼的事:。
移動(dòng)端的開(kāi)發(fā)中最煩的就是閃退瓢颅,公司的項(xiàng)目中雖然集成了Fabric
恩尾,但是只是簡(jiǎn)單的上傳崩潰信息,并沒(méi)有有效的防止閃退惜索。剛好最近在研究Method Swizzling
特笋,于是就想著是否可以攔截系統(tǒng)的異常剃浇,既可以不閃退也可以上傳崩潰信息巾兆?于是HTCrashReporter盛大登場(chǎng)猎物。關(guān)于HTCrashReporter今天暫不詳細(xì)介紹了,耽誤各位看官時(shí)間角塑,有興趣的看官蔫磨,可以先去全球最大程序猿交友網(wǎng)站交流一下,歡迎?star?圃伶。