今天遇到了一個引人第三方崩潰的問題,崩潰的信息是-[NSURLResponse statusCode]: unrecognized selector sent to instance 0x15785f230',趁機學習一下runtime的消息傳遞機制硼端。我把每次防止崩潰的時刻成為轉機~影暴,宗旨就是根據SEL找到IMP(讓方法找到實現(xiàn))。
根據obj_msgSend方法的調用順序曹鸠,做了如下的嘗試:
1.首先寫了一個NSURLResponse的category造成,因為NSURLResponse是個系統(tǒng)的類搞旭,無法在.m文件修改內容:
在這次轉機中,還是很好理解的后雷,特別需要注意的是resolveInstanceMethod中包含的IMP季惯,實現(xiàn)方法是個C語言函數(shù)吠各,該函數(shù)中必須包含有id和SEL兩個參數(shù)。
2.如果resolveInstanceMethod返回的是NO星瘾,便會拐個彎繼續(xù)根據SEL去找IMP走孽。根據函數(shù)的名字forwardingTarget惧辈。琳状。就是改變target。盒齿。改變目標念逞。。
在這次轉機中翎承,看來這個類已經對自己失望了,所以要去尋找別的類對象是否有對應的函數(shù)實現(xiàn)符匾。
2'' 在下面的例子中叨咖,我嘗試在forwardingTargetForSelector中返回是對象本身,在對象內部寫了個函數(shù)啊胶,就好像對象自己回心轉意了一下~~甸各,還是可以執(zhí)行的。我就暫且把它稱為回旋轉機吧焰坪。這樣做其實是不和常規(guī)的趣倾,完全可以在第一次轉機的時候。這里只是做個測試~
3.methodSignatureForSelector應該是讓對象最桑心的選擇了某饰,連其他對象都沒有想要的函數(shù)儒恋,只能把函數(shù)的簽名扔到茫茫人海中了。黔漂。就是將函數(shù)名稱包裝成簽名NSMethodSignature诫尽。方法簽名僅包含了參數(shù)類型和返回值。所以人家還需要調用forwardInvocation的調用才行炬守。
在這次轉機中牧嫉,forwardInvocation函數(shù)內部,可以有很豐富的封裝劳较【灾梗可以拿到函數(shù)名,獲取參數(shù)观蜗,設置返回值等臊恋。
以上所寫的內容,是為了屏蔽掉崩潰的問題墓捻。那么在平時的使用中抖仅,如果是想動態(tài)實現(xiàn)函數(shù)坊夫,還需要重載respondsToSelector中,將想要動態(tài)實現(xiàn)的函數(shù)返回為yes撤卢。不然調用respondeToSelector時环凿,會返回NO。
objc_msgSend的調用過程如下:
0.檢測這個selector是不是要忽略的放吩。()
1.首先檢測消息的接受者是否為nil智听,如果是nil,就不再發(fā)送消息了渡紫,所以nil對象執(zhí)行任何一個操作都不會崩潰到推。(插曲:曾經想要寫一個字符串的擴展,如果對象是nil惕澎,就輸出孔字符串@“”莉测。但是怎么也不生效。最后才恍然大悟唧喉,對象是nil捣卤,連消息都接收不到,怎么可能還變成@“”“诵ⅲ現(xiàn)在想想也是蠻有趣的董朝。)
///////////////////////////////////////////////////////////////////////////////////
如果是實例方法:
2.檢查class的緩存中是否有這個方法的實現(xiàn)。如果有唆阿,就調用益涧。(struct objc_cache *cache)指向最近使用的方法的指針,提高效率
3.若找不到驯鳖,就查找方法列表闲询。(struct objc_method_list **methodLists)方法地址列表,如CLS_CLASS (0x1L)浅辙,則存儲實例方法扭弧,如CLS_META (0x2L),則存儲類方法;
4.查看父類中的methodLists是否有該方法的實現(xiàn)记舆,如果沒有就再查找父類的父類中是否有該方法鸽捻。知道NSObject的根類為止。
///////////////////////////////////////////////////////////////////////////////////
如果是類方法:
2.會通過自己的isa找到自身類的metaClass(metaClass是用來存放類本身的泽腮,類方法就存儲在metaclass中的methodList中)御蒲,在methodList中查找
3.若找不到,就去查找metaClass的superClass诊赊,查找他的superClass(也是metaClass)的methodList中是否有對應函數(shù)厚满。
////////////////////////////////////////////////////////////////////////////////////
5.調用resolveInstanceMethod:(或者resolveClassMethod:)如果有,就返回yes碧磅。并調用
6.調用forwardingTargetForSelector函數(shù)碘箍,如果返回的對象中包含該方法遵馆,調用
7.調用methodSignatureForSelector函數(shù),如果返回了一個函數(shù)的簽名丰榴,再調用forwardInvocation货邓。根據里面的函數(shù),調用即可四濒。
8.調用doesNotRecognizeSelector换况。拋出異常
參考文章:
http://www.cnblogs.com/biosli/p/NSObject_inherit_2.html
http://my.oschina.net/u/566401/blog/182520
http://www.henishuo.com/runtime-message-forwarding/
http://blog.sunnyxx.com/2014/11/06/runtime-nuts/
最后峻黍,推薦一個我自己的產品复隆,找到我啦,可查看軌跡和定位姆涩,歡迎關注我的微信公眾號,時刻關注找到我啦的更新