(1)iOS7 — [UICollectionView _unhighlightAllItems],(2)Collection <__NSSetM: XXX> was mutated while being enumerated刊殉,(3)[NSIndexPath section] message sent to deallocated instance XXX

先上Crash堆棧信息:

Application Specific Information:
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSSetM: 0x16dab7a0> was mutated while being enumerated.'

Last Exception Backtrace:
0   CoreFoundation                  0x30a94f7e __exceptionPreprocess + 126
1   libobjc.A.dylib                 0x3b245cca objc_exception_throw + 34
2   CoreFoundation                  0x30a94a7c __NSFastEnumerationMutationHandler + 124
3   UIKit                           0x3381ed76 -[UICollectionView _unhighlightAllItems] + 126
4   UIKit                           0x334cc26e -[UICollectionView touchesBegan:withEvent:] + 362
5   UIKit                           0x332e662c -[UIWindow _sendTouchesForEvent:] + 316
6   UIKit                           0x332e16c6 -[UIWindow sendEvent:] + 754
7   UIKit                           0x332b68c8 -[UIApplication sendEvent:] + 192
8   UIKit                           0x332b4f72 _UIApplicationHandleEventQueue + 7098
9   CoreFoundation                  0x30a60206 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 10
10  CoreFoundation                  0x30a5f6d6 __CFRunLoopDoSources0 + 202
11  CoreFoundation                  0x30a5deca __CFRunLoopRun + 618
12  CoreFoundation                  0x309c8eba CFRunLoopRunSpecific + 518
13  CoreFoundation                  0x309c8c9e CFRunLoopRunInMode + 102
14  GraphicsServices                0x358ce65e GSEventRunModal + 134
15  UIKit                           0x33315148 UIApplicationMain + 1132
16  MyAppName                       0x00af441a main (main.m:14)
17  libdyld.dylib                   0x3b752ab2 tlv_initializer + 2
1殉摔、原因如下:

iOS7系統(tǒng),滑動(dòng)CollectionView的同時(shí)reloadData冗澈。

2钦勘、解決方案:

(1)對(duì)于只有1個(gè)section的UICollectionView陋葡,使用reloadSections代替reloadData亚亲,代碼如下:

    [UIView setAnimationsEnabled:animated];
    [collectionView performBatchUpdates:^{
        [collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
    } completion:^(BOOL finished) {
        [UIView setAnimationsEnabled:YES];
    }];

(2)對(duì)于有多個(gè)section的UICollectionView,則可以使用insertSections來替換reloadData,例如類似這樣的代碼:

    self.totalSections++;
    [self.mCollectionView performBatchUpdates:^{
        [self.mCollectionView insertSections:[NSIndexSet indexSetWithIndex:self.totalSections-1]];
    } completion:^(BOOL finished) {
        ...
    }];
3捌归、如果讀者有興趣肛响,可以閱讀以下的分析過程:

顯然,該堆棧為全系統(tǒng)棧惜索,沒有捕獲到App的任何代碼特笋,極大的增加了排查問題的范圍和難度,但比較幸運(yùn)的是巾兆,堆棧信息給出了錯(cuò)誤原因:Collection <__NSSetM: 0x16dab7a0> was mutated while being enumerated猎物。復(fù)制粘貼,谷歌一下角塑,大部分網(wǎng)友給出的原因是:在遍歷集合的同時(shí)蔫磨,對(duì)集合中的元素做了增刪改操作。類似于如下代碼:

for (NSString *obj in self.testArray) {
    ...
    [self.testArray addObject:@"4"];
    ...
}

但是圃伶,如果項(xiàng)目中真有這樣的代碼堤如,在開發(fā)調(diào)試的時(shí)候就應(yīng)該很容易暴露問題,不至于到線上才出現(xiàn)窒朋。那有沒有可能是多線程操作導(dǎo)致的呢搀罢?類似于如下代碼:

// 主線程
for (NSString *obj in self.testArray) {
    ...
}
    
// 子線程
[self.testArray addObject:@"4"];

順著這個(gè)思路,開始查找代碼中是否有多線程使用NSMutableSet的地方侥猩,經(jīng)過一番辛苦的代碼走查榔至,并沒有發(fā)現(xiàn)這樣的邏輯代碼,于是有點(diǎn)懷疑這個(gè)<__NSSetM: 0x16dab7a0>并不是App代碼創(chuàng)建的對(duì)象欺劳,而是系統(tǒng)框架內(nèi)部創(chuàng)建的洛退,那具體是哪個(gè)類呢?目前看來杰标,只能從全系統(tǒng)棧中尋找答案了兵怯。

從上往下逐行分析,發(fā)現(xiàn)與UICollectionView相關(guān)腔剂,谷歌一下:[UICollectionView _unhighlightAllItems]媒区,結(jié)果顯示,遇到此問題的人不止我一個(gè)掸犬,在Stack Overflow有高人給出了Crash復(fù)現(xiàn)路徑袜漩、原因猜測(cè)分析、以及問題解決方案:
(1)復(fù)現(xiàn)路徑:calling reloadData while the user is dragging the collection view.
(2)猜測(cè)分析:My guess is that there is a weak reference to the highlighted cell's indexPath somewhere inside of the collectionView, and that calling reload will dealloc that indexPath. When the collectionView then tries to unhighlight the cell, it crashes.
(3)解決方案:使用reloadSections代替reloadData湾碎。

為了驗(yàn)證這3點(diǎn)宙攻,尤其是(1)和(3),以確保問題從根本上得到解決介褥,我做了一個(gè)demo座掘,關(guān)鍵代碼如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.totalItems = 5;
    [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(testForCrash) userInfo:nil repeats:YES];
}

- (void)testForCrash {
    self.totalItems++;
    [self.cv_test reloadData];
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return self.totalItems;
}

開一個(gè)定時(shí)器递惋,間隔1秒鐘增加1個(gè)item,并且reloadData溢陪。這這個(gè)過程中萍虽,頻繁滑動(dòng)CollectionView,用不了很久形真,就會(huì)出現(xiàn)Crash:[NSIndexPath section] message sent to deallocated instance XXX(前提是開啟了Zombie Objects調(diào)試選項(xiàng))杉编。很顯然,訪問了一個(gè)已釋放的對(duì)象咆霜,并且從跟蹤的異常堆棧來看邓馒,是在遍歷NSMutableOrderedSet時(shí)發(fā)生了Crash,再結(jié)合Collection <__NSSetM: XXX> was mutated while being enumerated原因蛾坯,可以確定這個(gè)<__NSSetM: XXX>是UICollectionView內(nèi)部的一個(gè)NSMutableOrderedSet對(duì)象绒净。

Crash.png

注意:要復(fù)現(xiàn)這個(gè)Crash,必須在iOS7設(shè)備上運(yùn)行偿衰。據(jù)我不完全統(tǒng)計(jì)挂疆,線上的這個(gè)問題都只出現(xiàn)在iOS7系統(tǒng)上,其他系統(tǒng)(8下翎,9缤言,10)并沒有發(fā)現(xiàn)這個(gè)問題,并且我在iOS10的設(shè)備上運(yùn)行這個(gè)Demo视事,相同的操作并沒有復(fù)現(xiàn)這個(gè)Crash胆萧。有興趣的同學(xué)可以在8和9的系統(tǒng)上測(cè)試驗(yàn)證,由此可以猜測(cè)這個(gè)屬于iOS7系統(tǒng)特有的問題俐东。

由于iOS并沒有開放UICollectionView的源碼跌穗,對(duì)于第2點(diǎn)在此不做任何分析,以免誤導(dǎo)讀者虏辫,其實(shí)我也不懂蚌吸。

關(guān)于第3點(diǎn)提到的解決方案,親測(cè)可行砌庄。至于穩(wěn)定性如何羹唠,還得時(shí)間來驗(yàn)證。但這里想補(bǔ)充一點(diǎn)的是娄昆,如果這個(gè)UICollectionView有多個(gè)section佩微,則可以使用insertSections來替換reloadData,例如類似這樣的代碼:

self.totalSections++;
[self.mCollectionView performBatchUpdates:^{
    [self.mCollectionView insertSections:[NSIndexSet indexSetWithIndex:self.totalSections-1]];
} completion:^(BOOL finished) {
    ...
}];

再次說明:這個(gè)問題只有在iOS7的系統(tǒng)上才出現(xiàn)萌焰,也就是說哺眯,對(duì)于7之后的系統(tǒng)還是可以放心使用reloadData。

全文完扒俯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奶卓,一起剝皮案震驚了整個(gè)濱河市一疯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寝杖,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件互纯,死亡現(xiàn)場(chǎng)離奇詭異瑟幕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)留潦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門只盹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人兔院,你說我怎么就攤上這事殖卑。” “怎么了坊萝?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵孵稽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我十偶,道長(zhǎng)菩鲜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任惦积,我火速辦了婚禮接校,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狮崩。我一直安慰自己蛛勉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布睦柴。 她就那樣靜靜地躺著诽凌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪坦敌。 梳的紋絲不亂的頭發(fā)上皿淋,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音恬试,去河邊找鬼窝趣。 笑死,一個(gè)胖子當(dāng)著我的面吹牛训柴,可吹牛的內(nèi)容都是我干的哑舒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼幻馁,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼洗鸵!你這毒婦竟也來了越锈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤膘滨,失蹤者是張志新(化名)和其女友劉穎甘凭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體火邓,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丹弱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了铲咨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片躲胳。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖纤勒,靈堂內(nèi)的尸體忽然破棺而出坯苹,到底是詐尸還是另有隱情,我是刑警寧澤摇天,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布粹湃,位于F島的核電站,受9級(jí)特大地震影響泉坐,放射性物質(zhì)發(fā)生泄漏再芋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一坚冀、第九天 我趴在偏房一處隱蔽的房頂上張望济赎。 院中可真熱鬧,春花似錦记某、人聲如沸司训。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽壳猜。三九已至,卻和暖如春滑凉,著一層夾襖步出監(jiān)牢的瞬間统扳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國打工畅姊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留咒钟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓若未,卻偏偏與公主長(zhǎng)得像朱嘴,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容