CollectionView reloadSection方法出現[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:]

項目中碰到了這個問題,很坑爹
觸發場景:
一個CollectionView有多個section,每個section的數據源都通過單獨的網絡請求獲取,每次請求回來以后就刷新指定的section;
下拉刷新的時候,會同時并發這些section的網絡請求;
然后多刷幾次,就crash了,而且出現概率很高,crash的地方就是reloadSection,報的錯就是[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:]

經測試,如果直接用reloadData,是沒有問題的,但是界面感覺會閃,效率也有點低,理論上還是用reloadSection方法最好。

網上找了很多解決方案,試過用performPatchUpdates,試過提前調用numbersOfItemInSection,都不管用
最后終于在stackOverFlow上發現了可能的原因:
再reloadSection的時候,其他section必須保證不變,否則就會出現上面的問題。
對比我現在的場景,感覺應該就是這個原因了
因為多個section的網絡請求是并發的,每個請求回來都會reloadSection,那就可能會出現一個請求回來正在reloadSection的時候,另一個網絡請求也完成了,剛好修改了數據源,
雖然都是在主線程,但reloadSection方法應該不是瞬間就完成的,如果在reloadSection執行期間另外一個網絡請求修改了數據源,那就有問題了。

這個問題怎么解決呢?
1,每次都reloadData。 這個肯定是不能接受的,效果太差
2,讓服務器把這些網絡請求合并成一個。 這個我倒是可以接受,但服務器的同學肯定不能接受,而且改動起來也太大
3,現在采用的方案:客戶端把這些網絡請求合并成一個,然后統一reloadData。
其實也不是真正的合并請求,只是用一個dispatchGroup,將這些請求放到一個group中,等所有請求的結果都回來了再reloadData,看起來好像是合并了一樣。

具體實現:
在下拉刷新時創建一個group,設置group的完成通知時間為reloadData,然后 在每個section的網絡請求前,調用group.enter(),在其網絡請求的完成回調里,調用group.leave()。
注意:group.enter()和group.leave()必須成對出現,且在同一個線程調用;網絡請求不管成功或者失敗,都需要調用group.leave(),不然group就永遠完成不了了。

2017年11月29日更新:

創建的group在所有網絡請求的回調完成前都要保持對該group的引用,否則如果當回調時group已經被釋放,那就會發生badAccess的crash!!!
發生這種情況的原因應該是block對group的引用僅僅是弱引用,如果沒有其他強引用保證group不被釋放,group可能會在某些情況下會釋放,導致回調時調用group.leave()時group已經被釋放了,所以就crash了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,954評論 6 342
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結起來就是把...
    Dove_iOS閱讀 27,211評論 30 472
  • iOS網絡架構討論梳理整理中。。。 其實如果沒有APIManager這一層是沒法使用delegate的,畢竟多個單...
    yhtang閱讀 5,252評論 1 23
  • 概述在iOS開發中UITableView可以說是使用最廣泛的控件,我們平時使用的軟件中到處都可以看到它的影子,類似...
    liudhkk閱讀 9,089評論 3 38
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139