SocketRocket BAD_ACCESS 崩潰實(shí)例
在使用 SocketRocket 作為我們的 socket 服務(wù)的過程中签杈,不時(shí)的會(huì)收到一兩條 BAD_ACCESS 的崩潰崇摄。
崩潰的細(xì)節(jié)與 Github 上的兩個(gè) issues 相似:
SRProxyConnect Crash #587
Crashing at -[SRWebSocket stream:handleEvent:] (SRWebSocket.m:1405) #156
具體表現(xiàn)為 -[SRProxyConnect stream:handleEvent:]
或者 -[SRProxyConnect _configureProxy]
過程中出現(xiàn) BAD_ACCESS 崩潰聚凹。
在調(diào)用棧上看不出什么濒募,整個(gè)調(diào)用椇巧冢看起來(lái)比較正常刹前。但細(xì)看 crash 行數(shù),就會(huì)發(fā)現(xiàn)袱饭,都是在訪問self(SRWebSocket、SRProxyConnect)的時(shí)候報(bào)的crash呛占。
method執(zhí)行過程中self被釋放虑乖,基本可以肯定是線程問題。
以此為線索晾虑,結(jié)合 issue 156 的評(píng)論區(qū)疹味,發(fā)現(xiàn)代碼中有幾個(gè)問題。
其一是Socket釋放問題走贪。由于平臺(tái)設(shè)計(jì)佛猛,Socket的URL是與用戶 session 相關(guān)的,每次用戶 session 變化都會(huì)改變 socket 的 URL坠狡,而 SRWebSocket 的 URL 是在初始化的時(shí)候確定的,在 socket 生存期內(nèi)是只讀的遂跟。因此逃沿,我們采取的方案是在URL改變的時(shí)候斷開之前的 socket,并重新初始化一個(gè)新的幻锁。斷開連接代碼如下:
[_webSocket close];
_webSocket.delegate = nil;
_webSocket = nil;
這樣會(huì)導(dǎo)致socket斷開連接的過程中 _webSocket
就由于未被持有被直接釋放了凯亮,clean up 不到位。因此我們采用了 jtreanor 提到的措施使用一個(gè) SRWebSocketCloser
來(lái)負(fù)責(zé)持有 socket 直到 clean up 結(jié)束哄尔。
但這樣還不夠假消,我們還有幾個(gè) crash 也是與 Socket 服務(wù)相關(guān),其中一個(gè) unrecognized selector 崩潰岭接,調(diào)用棧在socket服務(wù)富拗,但 crash 原因卻是一個(gè)完全無(wú)關(guān)的類的 unrecognized,說(shuō)明當(dāng)前對(duì)象已經(jīng)被釋放并且被復(fù)寫鸣戴。
于是在 demo 中開了兩條線程做循環(huán)啃沪,并且開啟 Thread Sanitizer,壓測(cè)多線程的 data race窄锅。
經(jīng)過修復(fù)创千,壓測(cè)跑到 Xcode 崩潰 app 都沒崩┑( ̄Д  ̄)┍。