0x0 背景
原本是放到自己博客的葛超,不怎么用了,把文章同步過來浅辙,原文地址[iOS/OC]platform_memmove的Crash
這個問題是今年1月底排查的删顶,gif/apng動圖播放時在iOS11以下會小概率閃退。是個比較有趣的Crash闭专,重新記錄一下奴潘。
典型堆棧:
#13. Crashed: PINAnimatedImage disk write queue
0 libsystem_platform.dylib 0x1915d4e60 _platform_memmove + 96
1 ImageIO 0x1943c0a74 GIFReadPlugin::copyImageBlockSet(InfoRec*, CGImageProvider*, CGRect, CGSize, __CFDictionary const*) + 3192
2 ImageIO 0x1943c0a74 GIFReadPlugin::copyImageBlockSet(InfoRec*, CGImageProvider*, CGRect, CGSize, __CFDictionary const*) + 3192
3 ImageIO 0x1943bf7c4 GIFReadPlugin::CopyImageBlockSetProc(void*, CGImageProvider*, CGRect, CGSize, __CFDictionary const*) + 124
4 ImageIO 0x19422073c IIOImageProviderInfo::copyImageBlockSetWithOptions(CGImageProvider*, CGRect, CGSize, __CFDictionary const*) + 496
5 ImageIO 0x19421e640 IIOImageProviderInfo::CopyImageBlockSetWithOptions(void*, CGImageProvider*, CGRect, CGSize, __CFDictionary const*) + 356
6 CoreGraphics 0x1939eeb24 CGImageProviderCopyImageBlockSet + 220
7 CoreGraphics 0x193c833ac imageProvider_getBytes + 88
8 CoreGraphics 0x193ab30e0 CGDataProviderCopyData + 280
9 Pinterest 0x100a46238 __94+[PINAnimatedImageManager processAnimatedImage:temporaryDirectory:infoCompletion:decodedPath:]_block_invoke.225 (PINAnimatedImageManager.m:397)
10 libdispatch.dylib 0x1913d21fc _dispatch_call_block_and_release + 24
11 libdispatch.dylib 0x1913d21bc _dispatch_client_callout + 16
12 libdispatch.dylib 0x1913e012c _dispatch_queue_serial_drain + 240
13 libdispatch.dylib 0x1913d59a4 _dispatch_queue_invoke + 652
14 libdispatch.dylib 0x1913e08d8 _dispatch_queue_override_invoke + 360
15 libdispatch.dylib 0x1913e234c _dispatch_root_queue_drain + 572
16 libdispatch.dylib 0x1913e20ac _dispatch_worker_thread3 + 124
17 libsystem_pthread.dylib 0x1915db2a0 _pthread_wqthread + 1288
18 libsystem_pthread.dylib 0x1915dad8c start_wqthread + 4
該問題在Google上能搜到的結(jié)果還是不少的,但是有效信息很少影钉。比較有用的是openradar上提到的iOS10.3beta上已經(jīng)修復(fù)了該問題画髓。可以略微松一口氣平委,最不濟可以甩鍋給蘋果了奈虾。甚至如果你的APP只支持iOS11(可能性不大,但是萬一有呢)廉赔,都可以直接忽略了肉微。
0x1 Crash原因
雖然是系統(tǒng)的BUG,但是還是要搞清楚Crash的原因蜡塌,盡量在業(yè)務(wù)代碼上避免該問題引發(fā)其他的風(fēng)險碉纳。排查過程比較艱辛,只說最終結(jié)果吧:
CGContextDrawImage();不再保證對imageRef的原子操作馏艾。
當(dāng)對于1個imageRef劳曹,有多個線程并發(fā)繪制時,會觸發(fā)buffer的memcmp的Crash
根據(jù)問題原因可以知道琅摩,其實該Crash在多線程同時解碼同1份imageRef時才會Crash铁孵,因此如果沒有引入SDWebImage/YYImage等三方圖片庫,系統(tǒng)默認主線程解碼是不會有改Crash的房资。
另外蜕劝,靜圖解碼也是有概率Crash,但是網(wǎng)上反饋比較少志膀,主要原因也是概率的問題熙宇。和一般的多線程Crash一樣鳖擒,次數(shù)多了概率才打溉浙,在相同的業(yè)務(wù)場景下烫止,動圖解碼的頻次遠高于靜圖,因此動圖更容易觸發(fā)該Crash戳稽。
0x2 Crash防護
知道Crash原因后馆蠕,防護相對就比較簡單了,只要保證不會對同一個image資源同時解碼就可以了惊奇。1個簡單的方法是在解碼時互躬,使用UIImage作為input,對UIImage進行多線程保護颂郎。如:
@synchronized(image) {
return [self decodedImageWithCGImageRef:image.CGImage];
}