負(fù)責(zé)iOS版本質(zhì)量這段時(shí)間碰到了不少疑難崩潰汛蝙,它們的統(tǒng)一特點(diǎn)就是崩潰棧信息十分十分不明確豺撑,導(dǎo)致解決起來異常困難质帅。這里梳理和記錄一下那些曾經(jīng)令人頭禿的崩潰适揉。(不定期更新)
1、多線程操作NSMutable類型數(shù)據(jù)
1煤惩、NSMutableData appendBytes崩潰
5 Foundation __NSMutableDataGrowBytes + 272
6 Foundation -[NSConcreteMutableData appendBytes:length:] + 372
7 Foundation ___49-[_NSDispatchData enumerateByteRangesUsingBlock:]_block_invoke + 44
8 libdispatch.dylib __dispatch_data_apply + 128
9 libdispatch.dylib dispatch_data_apply + 40
10 Foundation -[_NSDispatchData enumerateByteRangesUsingBlock:] + 64
11 Foundation -[NSConcreteMutableData appendData:]
2嫉嘀、使用BlocksKit子線程遍歷__NSDictionaryM(NSMutableDictionary)崩潰
0 libobjc.A.dylib objc_retain + 8
1 MojiWeather __35-[NSDictionary(BlocksKit) bk_each:]_block_invoke (NSDictionary+BlocksKit.m:14)
2 CoreFoundation -[__NSDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:] + 232
3 MojiWeather -[NSDictionary(BlocksKit) bk_each:] (NSDictionary+BlocksKit.m:17)
4 MojiWeather -[AdvSystemNetworkManager p_handleNoAdResponse:] (AdvSystemNetworkManager.m:394)
5 MojiWeather __33-[GCDAsyncSocket closeWithError:]_block_invoke (GCDAsyncSocket.m:3143)
6 libdispatch.dylib __dispatch_call_block_and_release + 24
7 libdispatch.dylib __dispatch_client_callout + 16
8 libdispatch.dylib __dispatch_queue_serial_drain$VARIANT$mp + 528
9 libdispatch.dylib __dispatch_queue_invoke$VARIANT$mp + 340
10 libdispatch.dylib __dispatch_root_queue_drain_deferred_wlh$VARIANT$mp + 404
11 libdispatch.dylib __dispatch_workloop_worker_thread$VARIANT$mp + 644
12 libsystem_pthread.dylib _pthread_wqthread + 932
此類崩潰的共同點(diǎn)都很明顯
1、崩潰棧中出現(xiàn)了NSMutable為前綴的類對(duì)象魄揉。
2剪侮、崩潰棧均在子線程中調(diào)用。
觸發(fā)場(chǎng)景:
多個(gè)線程同時(shí)操作NSMutableData洛退、NSMutableArray時(shí)即容易出現(xiàn)崩潰瓣俯。
解決方案:
1、在串行隊(duì)列中進(jìn)行操作兵怯。
//在串行隊(duì)列中異步執(zhí)行
dispatch_async(serialQueue, ^{
//critical section彩匕,進(jìn)行數(shù)據(jù)的增刪改操作。
});
2媒区、加鎖驼仪。
//使用信號(hào)量加鎖
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
//critical section,進(jìn)行數(shù)據(jù)的增刪改操作袜漩。
dispatch_semaphore_signal(self.semaphore);
備注:
2绪爸、CTTelephonyNetworkInfo崩潰
0 libobjc.A.dylib objc_msgSend + 16
1 CoreTelephony _ServerConnectionCallback(__CTServerConnection*, __CFString const*, __CFDictionary const*, void*) + 52
2 CoreTelephony invocation function for block in CTServerState::sendNotification_sync(CTEvent, __CFString const*, __CFDictionary const*) const + 32
3 libdispatch.dylib __dispatch_call_block_and_release + 24
4 libdispatch.dylib __dispatch_client_callout + 16
5 libdispatch.dylib __dispatch_queue_drain + 1216
6 libdispatch.dylib __dispatch_queue_invoke + 132
7 libdispatch.dylib __dispatch_root_queue_drain + 664
8 libdispatch.dylib __dispatch_worker_thread3 + 108
9 libsystem_pthread.dylib _pthread_wqthread + 816
這個(gè)崩潰僅在iOS9及以下系統(tǒng)版本上出現(xiàn)。乍一看很令人費(fèi)解宙攻,但是仔細(xì)看能注意到崩潰棧中出現(xiàn)了CoreTelephony這個(gè)庫奠货,在iOS9以下版本上的CTTelephonyNetworkInfo對(duì)象有bug,已經(jīng)釋放的CTTelephonyNetworkInfo對(duì)象會(huì)接受通知并崩潰座掘。繼續(xù)排查項(xiàng)目递惋,發(fā)現(xiàn)舊版本Reachability里的CTTelephonyNetworkInfo不是類變量,會(huì)被多次初始化和釋放雹顺,存在崩潰風(fēng)險(xiǎn)丹墨。
觸發(fā)場(chǎng)景
多處調(diào)用舊版本的
[[Reachability reachabilityForLocalWiFi] isReachableViaWiFi]等方法。
解決方案
修改CTTelephonyNetworkInfo為類變量嬉愧,或者使用AFN里的AFNetworkReachabilityManager作為替代贩挣。
備注
CTTelephonyNetworkInfo should not be released
3、內(nèi)存泄露崩潰
0 libobjc.A.dylib objc_object::release() + 16
1 libobjc.A.dylib (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 844
2 libobjc.A.dylib (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 844
3 libdispatch.dylib __dispatch_last_resort_autorelease_pool_pop + 36
4 libdispatch.dylib __dispatch_root_queue_drain + 1308
5 libdispatch.dylib __dispatch_worker_thread3 + 120
6 libsystem_pthread.dylib _pthread_wqthread + 1176
通過調(diào)用棧AutoreleasePoolPage::pop,可以得知此時(shí)系統(tǒng)正在調(diào)用自動(dòng)釋放池的釋放操作(通過objc_object::release()也可以看出是對(duì)象內(nèi)存釋放時(shí)出現(xiàn)的崩潰)王财,因此判定是內(nèi)存泄露問題卵迂。
觸發(fā)場(chǎng)景
通過instrument分析,發(fā)現(xiàn)以下代碼有嚴(yán)重的內(nèi)存泄露問題:
MTLModel.m line 45: [obj validateValue:&validatedValue forKey:key error:error]
解決方案
參照鏈接绒净,給MTLModel增加分類方法见咒。
https://github.com/Mantle/Mantle/issues/818
https://github.com/Mantle/Mantle/issues/787#issuecomment-286608719
備注
內(nèi)存泄露這類問題不同的項(xiàng)目有不同的原因,建議多使用instrument的leaks排查挂疆,并通過崩潰趨勢(shì)來大致確認(rèn)出現(xiàn)問題的業(yè)務(wù)模塊改览。
4、后臺(tái)操作UIWindow問題
0 libobjc.A.dylib objc_retain + 16
1 UIKit ___39-[UIWindow _noteOverlayInsetsDidChange]_block_invoke + 132
2 UIKit __runAfterCACommitDeferredBlocks + 292
3 UIKit __cleanUpAfterCAFlushAndRunDeferredBlocks + 560
4 UIKit __afterCACommitHandler + 168
5 CoreFoundation ___CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
6 CoreFoundation ___CFRunLoopDoObservers + 372
7 CoreFoundation ___CFRunLoopRun + 1024
8 CoreFoundation CFRunLoopRunSpecific + 444
9 GraphicsServices GSEventRunModal + 180
10 UIKit -[UIApplication _run] + 684
11 UIKit UIApplicationMain + 208
12 MojiWeather main (main.m:16)
13 libdyld.dylib _start + 4
僅在iOS10以下設(shè)備出現(xiàn)缤言。崩潰棧完全得不到有用信息宝当,但是在Bugly上傳的頁面跟蹤數(shù)據(jù)中可以看出每次崩潰的頁面停留都是剛啟動(dòng)應(yīng)用時(shí)在二級(jí)Splash展示時(shí)崩潰,我們項(xiàng)目中的二級(jí)Splash是通過設(shè)置RootViewController的方式展示的胆萧,因此基本判斷是操作Splash的Window時(shí)出現(xiàn)的崩潰庆揩。繼續(xù)排查log,發(fā)現(xiàn)幾乎所有崩潰用戶在application:didFinishLaunchingWithOptions:方法中的launchOption的key值都是UIApplicationLaunchOptionsLocationKey跌穗,因此判斷該崩潰出現(xiàn)在用戶后臺(tái)定位發(fā)生改變時(shí)订晌,并不是用戶在前臺(tái)操作產(chǎn)生。
觸發(fā)場(chǎng)景
后臺(tái)定位功能觸發(fā)時(shí)蚌吸,應(yīng)用操作UIWindow導(dǎo)致锈拨。
解決方案
在application:didFinishLaunchingWithOptions:中判斷l(xiāng)aunchOption,如果是UIApplicationLaunchOptionsLocationKey方式進(jìn)入應(yīng)用羹唠,則不觸發(fā)二級(jí)Splash展示推励。即不操作UIWindow。
5肉迫、后臺(tái)OpenGL繪制問題
由于項(xiàng)目接入了cocos2d-objc,因此陸陸續(xù)續(xù)出現(xiàn)過各類后臺(tái)繪制問題稿黄。
這里統(tǒng)一梳理一下:只要崩潰棧中出現(xiàn)GLEngine喊衫、OpenGLES、gpusSubmitDataBuffers等相關(guān)字眼杆怕,即確認(rèn)為繪制問題族购。
觸發(fā)場(chǎng)景
這里的場(chǎng)景有兩種:1、同問題4陵珍,launchOption為UIApplicationLaunchOptionsLocationKey時(shí)其實(shí)應(yīng)用還在后臺(tái)寝杖,此時(shí)觸發(fā)OpenGL繪制即崩潰。
2互纯、用戶手動(dòng)進(jìn)入后臺(tái)之后(即調(diào)用UIApplication DidEnterBackground)瑟幕,繪制沒有停止。
解決方案
1、觸發(fā)后臺(tái)定位進(jìn)入引用時(shí)只盹,程序會(huì)走willFinishLaunchingWithOptions和didFinishLaunchingWithOptions辣往,但是不會(huì)走didBecomeActive,因此可以在didBecomeActive方法中增加標(biāo)志位殖卑,沒有走過didBecomeActive時(shí)站削,不觸發(fā)cocos2D的繪制。
2孵稽、在didEnterBackground增加停止繪制的操作许起。
備注
OpenGL ES May Not Be Used in Background Apps
6、Testflight包崩潰問題菩鲜。
測(cè)試使用Testflight包覆蓋Debug包园细,頻現(xiàn)崩潰,但是開發(fā)使用xcode Debug無法復(fù)現(xiàn)崩潰睦袖。導(dǎo)出設(shè)備崩潰日志發(fā)現(xiàn)崩潰棧信息為:
Application Specific Information:
abort() called
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0:
0 libsystem_kernel.dylib 0x000000021b8619fc __ulock_wait + 8
1 libdispatch.dylib 0x000000010c42d878 0x10c428000 + 22648
2 libdispatch.dylib 0x000000010c42d9b0 0x10c428000 + 22960
3 libdispatch.dylib 0x000000010c43bbb8 0x10c428000 + 80824
4 libdispatch.dylib 0x000000010c43b5d0 0x10c428000 + 79312
5 GPUToolsCore 0x000000010c4bc488 0x10c4ac000 + 66696
6 GPUToolsCore 0x000000010c4bf204 0x10c4ac000 + 78340
7 dyld 0x0000000109235504 ImageLoaderMachO::doModInitFunctions+ 103684 (ImageLoader::LinkContext const&) + 404
8 dyld 0x0000000109235738 ImageLoaderMachO::doInitialization+ 104248 (ImageLoader::LinkContext const&) + 36
9 dyld 0x0000000109230768 ImageLoader::recursiveInitialization+ 83816 (ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 464
10 dyld 0x000000010922f798 ImageLoader::processInitializers+ 79768 (ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 136
11 dyld 0x000000010922f854 ImageLoader::runInitializers+ 79956 (ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 84
12 dyld 0x000000010921e680 dyld::initializeMainExecutable+ 9856 () + 184
13 dyld 0x0000000109223468 dyld::_main+ 29800 (macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 4600
14 dyld 0x000000010921d044 _dyld_start + 68
解決方案
刪掉包珊肃,重新安裝TestFlight包進(jìn)行測(cè)試。