崩潰的截圖:
最近項目中有一個dispatch_group相關(guān)的巨坑(此問題解決方法很簡單坯临,難點是定位問題)焊唬,特此記錄下來并分享,希望幫助到有需要的人看靠!
本文涉及的知識面較廣赶促,有一定的邏輯性,如果想真正明白各種原理挟炬,請細(xì)細(xì)讀來鸥滨,如時間緊迫,可記住正確使用方法亦可谤祖!
閱讀本文需要的知識點:
1 熟悉AFNetworking
2 熟練使用dispatch_group
3 了解接口的含義
4 熟悉block工作原理
5 熟悉地址引用含義
先把crash場景和解決方案放在開頭:
crash場景:
1 有一組多個接口地址
2 利用 dispatch_group并發(fā)請求到數(shù)據(jù)后婿滓,統(tǒng)一回調(diào)(必要:利用AF進(jìn)行網(wǎng)絡(luò)請求)
3 頻繁調(diào)用2(必要)
4 每次調(diào)用2中的請求是同一組接口地址(必要)
注:頻繁的意思是一秒調(diào)用3次或3次以上
問題核心:
對dispatch_group進(jìn)行了額外的leave操作
問題代碼:
-(void)request{
dispatch_group_t group = dispatch_group_create();
self.group = group;
enter代碼....
[request requestGetUrl:url success:^(id responds) {
leave代碼....
}];
}
修正后代碼:
-(void)request{
if(self.group == nil){
dispatch_group_t group = dispatch_group_create();
self.group = group;
}
enter代碼....
[request requestGetUrl:url success:^(id responds) {
leave代碼....
}];
}
我想你肯定在疑惑加一個if判斷就解決了問題了,那這個坑應(yīng)該不大粥喜,分分鐘搞定凸主,請往下看
產(chǎn)生此問題的原因:概括的說是dispatch_group的原理和AFNetworking網(wǎng)絡(luò)請求回調(diào)block的緩存回調(diào)原理的協(xié)作問題
舉例詳細(xì)說明,流程圖如下:
正常情況下:執(zhí)行步驟1 的間隔時間充分長额湘,或者只執(zhí)行一次卿吐,此邏輯沒有問題旁舰,不會crash;
非正常情況:開頭所說的crash出現(xiàn)場景下嗡官,即頻繁執(zhí)行上圖中步驟1到步驟3
因為網(wǎng)絡(luò)請求的耗時和異步特性箭窜,這時候會發(fā)生一種情況
1 第一次在步驟1創(chuàng)建了一個新的group1,這時網(wǎng)絡(luò)請求n1到n4請求未返回衍腥,也就是b1到b4
還未調(diào)用绽快;
2 此時又執(zhí)行了一次步驟一創(chuàng)建了一個新的group,命名為group2(這是一個新的紧阔,代碼見上圖)坊罢,
并且賦值給了self.group,又執(zhí)行步驟二擅耽,對group2進(jìn)行enter活孩,發(fā)送網(wǎng)絡(luò)請求;
3 此時若是1中的網(wǎng)絡(luò)請求返回了,b1到b4就會調(diào)用乖仇,會對步驟2中創(chuàng)建的新的group2進(jìn)行
leave操作憾儒,當(dāng)2中的網(wǎng)絡(luò)請求返回,進(jìn)行回調(diào)時乃沙,會發(fā)生對group2進(jìn)行額外的leave操作起趾,
從而造成crash;
注:b1到b4因為leave的需要警儒,會對group進(jìn)行地址引用
作者受此文啟發(fā)训裆,雖場景不同,但原理一致蜀铲!
---2018-03-14更新錯誤代碼實例 --start
附1:
錯誤代碼實例(崩潰截圖見代碼下邊):
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
dispatch_group_t group = dispatch_group_create();
self.group = group;
dispatch_group_enter(self.group);
dispatch_group_enter(self.group);
dispatch_group_enter(self.group);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_group_leave(self.group);
sleep(2);
dispatch_group_leave(self.group);
dispatch_group_leave(self.group);
});
dispatch_group_notify(self.group, dispatch_get_main_queue(), ^{
NSLog(@"1234567");
});
}
修正后代碼:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
if(self.group == nil){
dispatch_group_t group = dispatch_group_create();
self.group = group;
}
dispatch_group_enter(self.group);
dispatch_group_enter(self.group);
dispatch_group_enter(self.group);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_group_leave(self.group);
sleep(2);
dispatch_group_leave(self.group);
dispatch_group_leave(self.group);
});
dispatch_group_notify(self.group, dispatch_get_main_queue(), ^{
NSLog(@"1234567");
});
}
---2018-03-14更新錯誤代碼實例 --end