說說NSObject的 performSelector 系列函數(shù)
記錄調試這個bug的過程
說說遇到的bug
?????說之前先說說遇到的bug,公司項目,Target最低終于支持iOS8以上道逗,web頁以前使用的是UIWebView,iOS8之后,蘋果推出了WKWebView,新寫的功能需要使用到WKWebView烫扼,在之前使用UIWebView的時候,H5為了兼容安卓和iOS,會做平臺類型判斷碍庵,這次團隊決定使用iOS,安卓和H5通用的一套javaScript和原生的APP交互SDK,最終挑選了DSBridge,文檔上說是一款三端易用的現(xiàn)代跨平臺 Javascript bridge映企, 通過它,你可以在Javascript和原生之間同步或異步的調用彼此的函數(shù)静浴,
????? 聯(lián)調過后卑吭,一切完美,同步马绝,異步方式都可以調用,但是最終打包給測試同學的時候挣菲,崩潰了富稻,經過一番檢查掷邦,發(fā)現(xiàn)在同步調用情況下崩潰,并且在DEBUG模式下沒有崩潰椭赋,在RElease模式下崩潰抚岗,javascript回調客戶端注冊的方法的時候直接 EXC_BAD_ACCESS 經過一番檢查,在同步或者異步方式回調的時候使用了 performSelector這個方法哪怔,先看看DSBridge部分源代碼:
-(NSString *)call:(NSString*) method :(NSString*) argStr {
...只貼重要部分
// 異步調用
SuppressPerformSelectorLeakWarning(
[JavascriptInterfaceObject performSelector:selasyn withObject:arg withObject:completionHandler];
);
// 同步調用
id ret;
SuppressPerformSelectorLeakWarning(
ret=[JavascriptInterfaceObject performSelector:sel withObject:arg];
);
}
?????DSBridge這樣設計宣蔚,會因為異步調用的時候客戶端通過completionHandler直接回調web端,而同步的時候得到一個返回值认境,在執(zhí)行js,通過js回調給web端,其中JavascriptInterfaceObject是客戶端注冊的調用對象胚委,可以看到使用了performSelector,同事確定release模式下崩潰叉信,我看到EXC_BAD_ACCESS 首先想到的是某個對象被釋放掉了亩冬,但是又給這個對象發(fā)了消息,但是日志都能正確硼身,一點一點定位位置硅急,直到無意中將同步調用方法的接收參數(shù)ret 去掉,才發(fā)現(xiàn)沒有崩潰了佳遂,確定本次崩潰和performSelector有關营袜。
?????仔細想想performSelector函數(shù)到底返回什么,說不清楚丑罪,NSObject.h中沒有注釋荚板,文檔中寫的返回值是void,函數(shù)定義的返回的是一個id類型。于是寫一個Demo測試巍糯,在debug模式下啸驯,給有個對象通過performSelector發(fā)送2個消息,這2個方法分別是方法A和方法B,方法A有返回值祟峦,方法B返回值為void罚斗,得到結果方法A的返回值是調用函數(shù)的返回值,調用方法B 直接崩潰宅楞。終于確定問題针姿。
?????為什么會崩潰呢,和調用方法是否有返回值有關厌衙,以前也知道performSelector編譯器不會對對象距淫,方法,進行檢驗婶希,會有內存泄露的可能產生榕暇,performSelector會把編譯時做的事情放到了運行時期,點擊這里去看一些performSelector的詳細介紹,因為不知道即將調用的selector是否有返回值彤枢,只有到了運行期才去檢測狰晚,調用了返回值為Void的一些函數(shù),相當于直接是 id obj = void,當然是OC語法不允許的缴啡,直接崩潰是必然的壁晒。
?????為什么我們自己的項目在Debug模式下正常呢,于是在項目中寫了局代碼測試,發(fā)現(xiàn)會返回一個nil业栅,猜測可能在哪個地方在進行了方法交換秒咐,查了代碼,沒有查到碘裕,可能是在某個SDK中携取,如圖:
總結
本次調試bug慢比較慢的原因:
- 對performSelector只知道怎么去用,細節(jié)部分沒想過娘汞,比如函數(shù)的返回值歹茶,將編譯時期可以報出的錯誤放到了運行時期,等等
- DSBridge源碼看過你弦,但是看的不仔細惊豺,花了好一會才了解整體調用過程。
- 查bug的時候會去猜測禽作,可能是哪個原因尸昧,然后通過注釋代碼來找問題(知識掌握的不牢固)
- 在使用第三方庫的時候一定要讀懂核心部分源碼,在遇到問題的時候才能夠快速定位問題旷偿。
歸根結底都是對知識掌握的不牢固烹俗,踩的坑還不夠多。
所有的bug都有他們必現(xiàn)的原因萍程,只是我沒有找到原因而已幢妄。
我以為的不一定是我以為的
????? 原本以為已經解決了這個問題,但是自己寫Demo的時候是在模擬器上測試茫负,公司項目是在真機上測試蕉鸳,都在真機上運行都沒有崩潰,寫的demo也返回了第一個固定參數(shù)忍法,公司項目傳入?yún)?shù){"action":"customservice"} 就直接崩潰 在真機上和在模擬器上有什么不同嗎潮尝?模擬器和真機都是iOS 11.3系統(tǒng),自己公司的項目為什么會發(fā)生崩潰呢饿序?