說(shuō)起死鎖, 可能很多人都懂其中的原理, 但是卻很少有人真正解決甚至遇到過(guò)死鎖, 最近有幸真的處理了一次死鎖.
<_NSCallStackArray 0x2825f4930>(
0 ??? 0x000000010af10578 0x0 + 4478535032,
1 Hago 0x0000000104712800 main + 0,
2 libdispatch.dylib 0x0000000107462ef4 dispatch_once_f + 212,
3 Hago 0x0000000104c6f474 +[HGNetworkMonitorSvc sharedObject] + 92,
4 Hago 0x0000000104c6e244 +[HGNetworkMonitorSvc isReachable] + 52,
5 Hago 0x0000000104968a24 -[HGTransmitService onNetworkingReachabilityDidChangeNotification:] + 76,
6 CoreFoundation 0x00000001844a0d00 <redacted> + 20,
7 CoreFoundation 0x00000001844a0ccc <redacted> + 64,
8 CoreFoundation 0x00000001844a01bc <redacted> + 392,
9 CoreFoundation 0x000000018449fe68 <redacted> + 96,
10 CoreFoundation 0x0000000184418310 <redacted> + 1404,
11 CoreFoundation 0x000000018449f8f4 _CFXNotificationPost + 696,
12 Foundation 0x0000000184eaa030 <redacted> + 68,
13 Hago 0x0000000104c6ef08 -[HGNetworkMonitorSvc init] + 1568,
14 Hago 0x0000000104c6f4dc __35+[HGNetworkMonitorSvc sharedObject]_block_invoke + 68,
15 libdispatch.dylib 0x0000000107460eac _dispatch_client_callout + 16,
16 libdispatch.dylib 0x0000000107462e98 dispatch_once_f + 120,
17 Hago 0x0000000104c6f474 +[HGNetworkMonitorSvc sharedObject] + 92,
18 Hago 0x0000000104c75678 -[HGAppModule handleFirstPageAppearCallback] + 52,
19 Hago 0x0000000104c75144 __25-[HGAppModule initModule]_block_invoke_2 + 48,
20 libdispatch.dylib 0x0000000107460eac _dispatch_client_callout + 16,
21 libdispatch.dylib 0x0000000107462e98 dispatch_once_f + 120,
22 Hago 0x0000000104c750b0 __25-[HGAppModule initModule]_block_invoke + 196,
23 Hago 0x0000000104c75594 __24-[HGAppModule delayInit]_block_invoke + 636,
24 libdispatch.dylib 0x0000000107460eac _dispatch_client_callout + 16,
25 libdispatch.dylib 0x0000000107462e98 dispatch_once_f + 120,
26 Hago 0x0000000104c752d8 -[HGAppModule delayInit] + 164,
27 Hago 0x00000001046bd06c __36-[HGTabBarController viewDidAppear:]_block_invoke + 88,
28 libdispatch.dylib 0x0000000107460eac _dispatch_client_callout + 16,
29 libdispatch.dylib 0x0000000107462e98 dispatch_once_f + 120,
30 Hago 0x00000001046bcfe8 -[HGTabBarController viewDidAppear:] + 408,
31 UIKitCore 0x00000001b114b1e4 <redacted> + 808,
32 UIKitCore 0x00000001b0d3df38 <redacted> + 200,
33 Hago 0x0000000104a19434 -[HGNavigationViewController viewDidAppear:] + 80,
34 UIKitCore 0x00000001b114b1e4 <redacted> + 808,
35 UIKitCore 0x00000001b114b600 <redacted> + 264,
36 CoreFoundation 0x0000000184418754 <redacted> + 144,
37 UIKitCore 0x00000001b114b390 <redacted> + 1236,
38 UIKitCore 0x00000001b114db78 <redacted> + 44,
39 UIKitCore 0x00000001b114c1bc <redacted> + 92,
40 UIKitCore 0x00000001b0a8f848 <redacted> + 564,
41 UIKitCore 0x00000001b0a7d77c <redacted> + 384,
42 UIKitCore 0x00000001b0a9e2b4 <redacted> + 152,
43 CoreFoundation 0x00000001844c23f4 <redacted> + 20,
44 CoreFoundation 0x00000001844c1cf4 <redacted> + 272,
45 CoreFoundation 0x00000001844bcce8 <redacted> + 1060,
46 CoreFoundation 0x00000001844bc5a4 CFRunLoopRunSpecific + 436,
47 GraphicsServices 0x000000018672a584 GSEventRunModal + 100,
48 UIKitCore 0x00000001b0a83b04 UIApplicationMain + 212,
49 Hago 0x00000001047128a4 main + 164,
50 libdyld.dylib 0x0000000183f7cdc8 <redacted> + 4
這次死鎖的原因是HGNetworkMonitorSvc是一個(gè)單例, 但是我卻在這個(gè)單例里面發(fā)了一個(gè)通知, 更不巧的是, 這個(gè)通知又用到了HGNetworkMonitorSvc這個(gè)單例, 就導(dǎo)致了死鎖.
#define IMPLEMENT_SIGNALTON(__TYPE__) \
+ (instancetype)sharedObject { \
static dispatch_once_t __once; \
static __TYPE__ * __instance = nil; \
dispatch_once(&__once, ^{ \
__instance = [[__TYPE__ alloc] init]; \
}); \
return __instance; \
}
相信100個(gè)iOS開(kāi)發(fā)99個(gè)都是用這種方式來(lái)創(chuàng)建單例的吧, 但是dispatch_once在進(jìn)行實(shí)例初始化的時(shí)候, 實(shí)際上對(duì)__once進(jìn)行了加鎖, 這里剛好在init方法里做了如下的操作.
- (instancetype)init
{
if (self = [super init]) {
GLobalRealReachability.autoCheckInterval = 10.f;
GLobalRealReachability.pingTimeout = 10;
[GLobalRealReachability startNotifier];
ReachabilityStatus status = GLobalRealReachability.currentReachabilityStatus;
WWANAccessType wwanType = GLobalRealReachability.currentWWANtype;
GLobalRealReachability.hostForPing = @"https://i-863.ihago.net/d/_ping";
_networkType = [self getPKNetworkTypeFromRealStatus:status wwanType:wwanType];
_realNetworkType = _networkType;
MFLogInfo(@"NetWork", @"network initial:%@", @(_networkType));
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(networkChanged:)
name:kRealReachabilityChangedNotification
object:nil];
PKNetworkType aPreNetworkType = [self refreshNetworkType:status wwanType:wwanType];
[BS_NotifyCenter postNotificationName:kMFNetworkingReachabilityDidChangeNotification
object:nil
userInfo:@{ kMFNetworkingReachabilityNotificationStatusItem : @(self.networkType),
kMFNetworkingReachabilityNotificationStatusBeforeItem : @(aPreNetworkType) }];
[self initNetworkMonitor];
}
return self;
}
kMFNetworkingReachabilityDidChangeNotification通知最終會(huì)觸發(fā)
- (void)onNetworkingReachabilityDidChangeNotification:(NSNotification *)notification
{
BOOL isReachable = [HGNetworkMonitorSvc isReachable];
MFLogInfo(LogTag, @"onNetworkingReachabilityDidChangeNotification:%d", isReachable);
PKNetworkType beforeStatus = (PKNetworkType)[notification.userInfo[kMFNetworkingReachabilityNotificationStatusBeforeItem] longLongValue];
if (isReachable && beforeStatus == PKNetwork_NotReachable) {
[self onChannleOrNetReady];
} else if (!isReachable && beforeStatus != PKNetwork_Unknown) {
[self onChannleOrNetBreak];
}
}
HGNetworkMonitorSvc.m
+ (BOOL)isReachable
{
return [HGNetworkMonitorSvc sharedObject].realNetworkType != PKNetwork_Unknown &&
[HGNetworkMonitorSvc sharedObject].realNetworkType != PKNetwork_NotReachable;
}
這里又用了HGNetworkMonitorSvc, 就造成了死鎖, 因?yàn)槎际窃谥骶€程執(zhí)行的, 在init前, 系統(tǒng)對(duì)__once加鎖, 當(dāng)init經(jīng)過(guò)一波操作同步調(diào)用了HGNetworkMonitorSvc的sharedObject, 這時(shí)候__once已經(jīng)加鎖, 無(wú)法訪問(wèn), 主線程因此卡死, 死鎖行程.
解決的辦法, 應(yīng)該盡量避免在單例的init里發(fā)通知, 即便發(fā)通知也最好dispatch一下
dispatch_async(dispatch_get_main_queue(), ^{
[BS_NotifyCenter postNotificationName:kMFNetworkingReachabilityDidChangeNotification
object:nil
userInfo:@{ kMFNetworkingReachabilityNotificationStatusItem : @(self.networkType),
kMFNetworkingReachabilityNotificationStatusBeforeItem : @(aPreNetworkType) }];
});
這樣就會(huì)把通知放到下次runloop去執(zhí)行, 不會(huì)阻塞當(dāng)前線程了.