iOS NSPROXY ARC填坑記

        謹以此文紀念多個日夜不停定位問題填坑的日子。

        近期,有個bug映入眼簾,主要是某段程序對http的數據進行截取,用於進行二次數據分析,然後截取數據的方式主要對系統自帶的方式發送請求可以截獲,也可以回調,用第三方的庫則不能回調,不知道哪裏環節出來問題。

首先,蘋果在 iOS9 之後已經放棄了 NSURLSession所以在現在的實際開發中,一般使用的是 iOS7 之後推出的NSURLSession。

https://www.jianshu.com/p/a8fc22afb739對NSURLSession進行了比較全面的講解,另外,AFNetWorking的源碼解析也比較多,比如:

https://www.cnblogs.com/polobymulberry/p/5081049.html 這個非常推薦,博主寫了大量的中文註釋,基本上把主要的方法都逐個講解,良心之作

https://www.jianshu.com/p/b98cf91b9ce2

https://www.codercto.com/a/105729.html

基本上是在session的基礎上進行了封裝,包括各種代理的回調、跟UI的互動、安全性、序列化等,這裏主要關注對代理的回調,

        第一反應,是http截取的代碼與第三方庫的代碼存在衝突

        於是將截取數據模塊代碼和第三方庫(AFNetWork)的代碼進行了全盤瀏覽,發現,兩者代碼比較相近🧐,事情貌似不簡單,初步懷疑是部分代碼重複,或者多次hook導致的問題,於是對兩者的單例、線程安全、死鎖、Hook等地方進行了重點探尋,結果,日子一天一天過,兩天後一無所獲。。。。。。。

事實上AFNetWork的穩定安全一直比較不錯,甚至很多方法都加了sately前綴,比如

     單例、鎖、安全前綴一應俱全

    好吧,還是老老實實從復現場景-->鎖定代碼-->找到問題的順序一步一步走

   爲了好定位,直接將AFNetWork的源碼倒入,debug走起

    先用官方request實現一波

一個簡單的NSURLSession的request請求,上報情況能夠通過回調方法進行展示

在用AFNetwork的方式實現一波

這一次,回調沒有反應。現象比較好復現

下一步,定位代碼

AFNetwork的代碼層數比較多,但核心的方法不多,網上有很多的源碼講解的文章,大家可以參考下

https://www.jianshu.com/p/a360140bf220 ,還有很多分章節分類名進行解析,這裏就不一一分享了

,通過get方法回溯,找到AFNetwork對應使用NSURLSession建立連接的地方

這裏有個知識點AFURLSessionManager是AFHTTPSessionManager父類,NSURLSession的申請在AFURLSessionManager進行了實現,如下

可以看到AFNetwork也使用了session,只是實現的方式不一樣,除了加入默認配置文件,還實現了delegate的方式

調用時datataskwithRequest進行了添加。

那如果直接使用這種方式可以回調嗎?

如圖:

居然回調不起作用了!

至此,AFNetwork表示,這鍋我不背🧐

那目標明確了,就是http截取的業務代碼了

一般來說,在控制變量法的原則下,按照先變配置模塊、再變初始化模塊、接着變主路徑、最後變分模塊定位的順序依次排查。

從啓動模塊開始排查,發現首先進入配置初始化,

打開看下配置內容

看起來就第一個比較可疑

關掉試下

居然成功回調了!

找下allowInterveneNetwork涉及的地方

主要有三個方法

xxx_urlSessionTaskDidStart,xxx_urlSessionTaskDidStop,xxx_sessionWithConfiguration

 通過命名就可以看出來,第一個是task任務開始前,第二個任務是task結束後,第三個任務是進行初始配置

三個方法分別關閉,逐個回調情況

xxx_urlSessionTaskDidStart和xxx_urlSessionTaskDidStop分別關閉時偶爾出現回調,一起關閉時偶爾出現回調

xxx_sessionWithConfiguration關閉時,偶爾出現回調

。。。看來比較玄學。。。問題又一次陷入了僵局

逐個看代碼

xxx_sessionWithConfiguration對應的一段代碼吸引了我

xxx_sessionWithConfiguration是用來替換系統方法sessionWithConfiguration,這裏剛好與前期沒有回調的方法匹配

,看來問題大概率就在這裏了

xxx_sessionWithConfiguration大概意思是創建了一proxy的實例,將系統原生的delegate進行封裝,而後進行傳遞

,按照控制變量法,將封裝去掉,看看回調是否成功

成功了!

看來就是這了

接着看proxy和delegate之間的聯繫

proxy是nsproxy的子類

通過一個弱引用,引用delegate

這裏一個知識點:nsproxy作爲代理更爲輕量,因爲

  • NSProxy是一個抽象的基類,是根類,與NSObject類似

  • NSProxyNSObject都實現了<NSObject>協議

NSObject的所有Category中定義的方法無法在繼承NSObject的代理中完成轉發,具體參考

http://tanhao.me/code/160702.html/
這裏使用了nsproxy主要在轉發時,進行http截取,然後再進行真正的轉發

既然做轉發,就在每次轉發時打印一下log,看看有沒有回到原始的delegate

結果比較詫異!

proxy引用的delegate變爲空!

怪不得delegate沒反應

現在的情況是變成

proxy--->delegate 時 delegate爲空

dalagate單獨使用,可以正常使用

查看一下proxy裏面的代碼,確認沒有置空的操作

所以問題變成了delegate怎麼沒的

按照內存愛好者的常用思想,弱引用是否有問題,其實proxy一般來說使用弱引用,這樣可以解決一些定時器等循環引用的問題

https://blog.csdn.net/shubinniu/article/details/80895450

問題又一次陷入了僵局

😂

最後,不死心試下將proxy--->delegate 的弱引用變成強引用

成功了!

什麼原因呢?

再分析一次

proxy--->delegate 弱引用時 delegate被置空

proxy——>delegate 強引用時 delegate有效,可以正常使用

dalagate單獨使用,可以正常使用

proxy裏面沒有置空delegate的操作

所以,誰置空了delegate!

推理大師說過,除所有不可能的,剩下的那個即使再不可思議,那也是事實

不要懷疑,是的,這是一個系統bug!!!

 

NSProxy在ARC下,弱引用的屬性,會被強制置空,不要懷疑,親測有效

https://joris.kluivers.nl/blog/2012/03/26/weak-references-to-nsproxy-with-arc/

https://oomake.com/question/2426222

https://stackoverflow.com/questions/9104544/how-can-i-get-ocmock-under-arc-to-stop-nilling-an-nsproxy-subclass-set-using-a-w

大家可以嘗試一下,刺激。。。

 

拓展知識1:

對於http的截取分析,有兩個思路,一個是AFNetWork的思路,通過獲取代理和HOOK  resume 和suspend的方式,一種是https://www.jianshu.com/p/ae5e8f9988d8的方法,其實兩種方式各有利弊,AFN的方法是大部分第三方庫的方式,“緊貼”系統方法,與系統交互較多,能夠更多的進行性能分析,而後面這種思路“緊貼”業務,可以對前後端的數據進行業務級過濾,重定向等,看具體業務需要

拓展知識2:

關於網絡監控ios版,有些比較優秀的可以參考

https://www.jianshu.com/p/3bdb027a63c7

http://code4app.com/blog-822717-1166.html

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章