謹以此文紀念多個日夜不停定位問題填坑的日子。
近期,有個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
類似 -
NSProxy
和NSObject
都實現了<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
大家可以嘗試一下,刺激。。。
拓展知識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