我的代碼我的坑(十五) —— dispatch_once導致的死鎖的問題(一) 版本記錄 前言 問題描述 問題分析 問題解決 後記

版本記錄

版本號 時間
V1.0 2021.08.18 星期三

前言

做了好幾個APP,碰到了大大小小的很多坑,以前碰到坑,解決了就結束了,這裏想把自己碰到的坑記錄下來,一來給自己備查二來希望可以幫助到大家。感興趣的可以關注下,也歡迎大家補充留言,感興趣的看上面幾篇文章。
1. 我的代碼我的坑(一) —— 自簽名證書導致請求取消的問題(一)
2. 我的代碼我的坑(二) —— UIImageView動畫點擊後動畫和圖片消失的問題(一)
3. 我的代碼我的坑(三) —— iOS9系統WKWebView加載頁面白板的問題(一)
4. 我的代碼我的坑(四) —— iOS12系統自定義漸變色UISwitch手機橫屏的異常問題(一)
5. 我的代碼我的坑(五) —— 不可編輯狀態的UITextView文本高度大於視圖高度默認滾動到底部的問題(一)
6. 我的代碼我的坑(六) —— UITextField輸入長度自動截取時漢字和拼音帶來的末位截取不能正常輸入漢字的問題(一)
7. 我的代碼我的坑(七) —— UIImageView做序列幀動畫結束後沒有回調並且“隱藏”(一)
8. 我的代碼我的坑(八) —— iOS 13.1.2 Debug調試模式系統layoutSubviews中修改frame循環調用導致的崩潰(一)
9. 我的代碼我的坑(九) —— 系統鍵盤拼音全鍵無法正常聯想以及輸入漢字高亮區識別不計入長度計數的問題(一)
10. 我的代碼我的坑(十) —— iOS9 Xib實例化的UITableViewCell中UIButton和UISwitch等控件的IBAction點擊無響應的問題(一)
11. 我的代碼我的坑(十一) —— macOS Mojave 和 xcode 11.1 (11A1027)環境下運行iphonex以上流海屏xcode install時xocde崩潰閃退的問題(一)
12. 我的代碼我的坑(十二) —— iOS10字體DIN Condense Bold字體顯示不全頂部被切割的問題(一)
13. 我的代碼我的坑(十三) —— 狀態欄高度的動態計算(一)
14. 我的代碼我的坑(十四) —— Xcode 12.5中React編譯不過的問題(一)

問題描述

首先我們看一段崩潰的堆棧:

Application Specific Information:
BUG IN CLIENT OF LIBDISPATCH: trying to lock recursively

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libdispatch.dylib               0x0000000186597c9c _dispatch_once_wait$VARIANT$armv81.cold.1 + 28
1   libdispatch.dylib               0x0000000186560da8 _dispatch_gate_broadcast_slow$VARIANT$armv81 + 0
2   OneTravel                       0x000000010504cee8 +[DCLocationManager sharedInstance] + 5213928 (DCLocationManager.m:86)
3   OneTravel                       0x0000000104eb54f4 -[DCDOrderDurationReportService checkArrive] + 3544308 (DCDOrderDurationReportService.m:0)
4   CoreFoundation                  0x00000001868b9e8c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 20
5   CoreFoundation                  0x00000001868b9e4c ___CFXRegistrationPost_block_invoke + 48
6   CoreFoundation                  0x00000001868b9438 _CFXRegistrationPost + 400
7   CoreFoundation                  0x00000001868b8e30 _CFXNotificationPost + 696
8   Foundation                      0x0000000187b2b934 -[NSNotificationCenter postNotificationName:object:userInfo:] + 60
9   OneTravel                       0x0000000105050828 -[DCLocationManager didiLocationManagerDidUpdateDistance:distanceFilter:] + 5228584 (DCLocationManager.m:772)
... ...

問題分析

具體原因有描述,很清晰,是trying to lock recursively,其實就是鎖住了。具體什麼原因鎖住了呢,就得具體分析了,看下最上面一行:

0   libdispatch.dylib               0x0000000186597c9c _dispatch_once_wait$VARIANT$armv81.cold.1 + 28

其實就是_dispatch_once_wait導致的崩潰。再看其他行的堆棧可以看見就是[DCLocationManager sharedInstance]這個單例,
但是具體分析,爲什麼單例會有死鎖呢?

在網上搜索了下,確實有人遇到過,比如下面:

上面那篇文章主要問題代碼如下:

//AppManager.m
+ (AppManager *)getAppManager {
    static AppManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[AppManager alloc] init];
        
        ViewController *vc = [[ViewController alloc] init];
        UIWindow *window = [UIApplication sharedApplication].keyWindow;
        window.rootViewController = vc;
        [vc doSomeThing];
    });
    return manager;
}
//AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    [self.window makeKeyWindow];
    
    _manager = [AppManager getAppManager];
    
    [self.window makeKeyAndVisible];
    
    return YES;
}
//viewController.m
- (void)doSomeThing {
    [AppManager getAppManager];
    self.view.backgroundColor = [UIColor cyanColor];
}

其實說直接點就是:

在單例實例化返回單例對象之前,又調用了該單例對象。

比如上面那個文章就是AppManager單例在實例化返回之前,在VC中又調用了單例對象,造成了死鎖。

回到了我們這個項目,分析了下,有下面幾段代碼:

static DCLocationManager *sharedInstance = nil;

+ (instancetype)sharedInstance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = DCLocationManager.new;
    });
    return sharedInstance;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
    ... ...
     [[NSNotificationCenter defaultCenter] postNotificationName:kNotification_DCLocationUpdated object:nil];      }
    ... ...

這裏看是一個通知沒有什麼,但是在監聽這個通知的地方,就又調用了[DCLocationManager sharedInstance]這個單例。這樣在單例沒有實例化完成之後就又調用了這個單例,造成了死鎖。

所以這裏是一個經驗教訓:

在單例進行實例化,init方法裏儘量少進行模塊間通信,這樣邏輯以後很複雜時,就又可能調用這個單例了。


問題解決

這裏改動了幾個地方,一個就是上面發送通知的地方,用dispatch_async包起來,等下一個runloop在發送。

dispatch_async(dispatch_get_main_queue(), ^{
      [[NSNotificationCenter defaultCenter] postNotificationName:kNotification_DCLocationUpdated object:nil];
});

還有很多地方用[DCLocationManager sharedInstance]獲取對象屬性的地方,改爲[DCLocationManager xxxx]類方法。

後記

本篇主要講述了dispatch_once導致的死鎖的問題,感興趣的給個贊或者關注~~~

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