在滚动和重新加载时,UICollectionViewFlowLayout子类崩溃访问超出边界的数组 [英] UICollectionViewFlowLayout subclass crashes accessing array beyond bounds while scrolling and reloading

查看:1162
本文介绍了在滚动和重新加载时,UICollectionViewFlowLayout子类崩溃访问超出边界的数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个UICollectionView,它使用UICollectionViewFlowLayout的自定义子类,使部分标题贴在屏幕顶部,同时像UITableView的简单样式一样滚动。我的代码基于这种方法:

I have a UICollectionView that uses a custom subclass of UICollectionViewFlowLayout to make the section headers stick to the top of the screen while scrolling just like UITableView's plain style. My code is based on this approach:

http://blog.radi.ws/post/32905838158/sticky-headers-for-uicollectionview-using

此外,集合视图设置为在用户滚动到底部时加载更多结果。也就是说,当用户到达集合视图的底部时,Web请求会加载更多数据,然后重新加载集合视图,即调用reloadData。

In addition, the collection view is set up to load more results when the user scrolls to the bottom. That is, when the user gets to the bottom of the collection view, a web request loads more data and then reloads the collection view, i.e. calling reloadData.

似乎除了我通过运行iOS 7和7.1的TestFlight从beta测试人员那里得到一些崩溃报告之后,他们说当他们快速向下滚动到底部(即触发更多结果加载)时会发生以下情况:

It seems to work well except I've been getting a few crash reports from beta testers via TestFlight running iOS 7 and 7.1 who say that the following happens occasionally when they scroll down to the bottom (i.e. triggering more results to load) quickly:

*** -[__NSArrayM objectAtIndex:]: index 28 beyond bounds [0 .. 16]
PRIMARY THREAD THREAD 0

__exceptionPreprocess
objc_exception_throw
-[__NSArrayM objectAtIndex:]
-[UICollectionViewFlowLayout(Internal) _frameForItemAtSection:andRow:usingData:]
-[UICollectionViewFlowLayout layoutAttributesForItemAtIndexPath:usingData:]
-[UICollectionViewFlowLayout layoutAttributesForItemAtIndexPath:]
-[MyCustomCollectionViewFlowLayout layoutAttributesForItemAtIndexPath:]
-[MyCustomCollectionViewFlowLayout layoutAttributesForSupplementaryViewOfKind:atIndexPath:]
-[MyCustomCollectionViewFlowLayout layoutAttributesForElementsInRect:]
-[UICollectionViewData validateLayoutInRect:]_block_invoke
-[UICollectionViewData validateLayoutInRect:]
-[UICollectionView layoutSubviews]
-[UIView(CALayerDelegate) layoutSublayersOfLayer:]
-[CALayer layoutSublayers]

似乎是虽然当我的自定义流布局代码调用 [self.collectionView numberOfItemsInSection:someSection] 来获取某个部分最后一项的布局属性时,该调用将根据新加载的数据返回(例如在这种情况下,一个部分现在有29个项目)但是默认流程布局的内部仍然使用某种缓存数据(例如,在这种情况下,该部分只有17个项目)。不幸的是,我无法自己重现崩溃,即使是经验丰富的beta测试人员也无法一致地重现它。

It seems as though when my custom flow layout code calls [self.collectionView numberOfItemsInSection:someSection] to get the layout attributes of a section's last item, that call returns based on the newly-loaded data (e.g. in this case that a section now has 29 items) but the internals of the default flow layout are still using some kind of cached data (e.g. in this case that the section only has 17 items). Unfortunately, I can't reproduce the crash myself and even the beta testers who have experienced it can't reproduce it consistently.

有什么想法会发生什么?

Any ideas what's going on there?

推荐答案

编辑2 ,根据第2条BenRB的评论。

Edit 2, as per 2nd BenRB's comment.

当dataSource更新并调用reloadData时,后者确实使CV中的所有内容无效。
但是,启动的刷新过程的逻辑和确切序列发生在默认流布局中,并且对我们隐藏。

When dataSource gets updated and you call reloadData, this latter really invalidates everything in CV. However the logic and the exact sequence of the initiated refresh process happens inside the default flow layout and is hidden from us.

特别是,默认流布局有自己的私有 _prepareLayout (确切地说,使用下划线)方法,这是独立于 prepareLayout 及其子类提供的重载。

In particular, the default flow layout has its own private _prepareLayout (exactly, with underscore) method, which is independent from the prepareLayout and its overloads provided by the subclasses.

prepareLayout 's(没有下划线)顺便说一句,基本流布局类的默认实现什么都不做。

prepareLayout's (without underscore) default implementation of the base flow layout class does nothing, by the way.

在刷新过程中,默认流布局为其子类提供了通过 layoutAttributesForElementsInRect提供更多信息(例如附加layoutAttributes)的机会: layoutAttributesForItemAtIndexPath:callbacks。为了保证基类的数据和相应的indexPath / layoutAttributes数组之间的一致性,只能在这些相应的方法中调用相应的超级:

During the refresh process the default flow layout gives its subclass a chance to provide more information (e.g. additional layoutAttributes) through layoutAttributesForElementsInRect: and layoutAttributesForItemAtIndexPath: "callbacks". To guarantee the consistency between base class' data and respective indexPath / layoutAttributes array, calls to corresponding "super" should only happen inside these respective methods:


  • [super layoutAttributesForElementsInRect:] 仅在
    内重载 [layoutAttributesForElementsInRect:]

[super layoutAttributesForItemAtIndexPath:] 仅在重载的 [layoutAttributesForItemAtIndexPath:]

这些方法之间不应发生交叉调用,至少indexPaths不是由提供的他们相应的超级方法,因为我们不知道究竟发生了什么

No cross-calls between these methods should happen, at least with indexPaths not supplied by their corresponding "super" methods, because we don't know exactly what happens inside.

我很长时间都在与我的简历作斗争最后以工作顺序结束:

I was fighting with my CV for a long time and ended with the only working sequence finally:


  1. 通过直接访问准备add'l布局数据dataSource(没有调解CV的numberOfItemsInSection :),并将该数据存储在子类'对象中,例如在字典属性中,使用indexPath作为键。我在重载的 [prepareLayout] 中执行此操作。

将存储的布局数据提供给基础通过回调请求此信息的类:

Supply the stored layout data to the base class when it requests this information through callbacks:

// layoutAttributesForElementsInRect

// layoutAttributesForElementsInRect

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {

//calls super to get the array of layoutAttributes initialised by the base class
**NSArray *array = [super layoutAttributesForElementsInRect:rect];**

for(MyLayoutAttributes *la in array)

    if(la.representedElementCategory == UICollectionElementCategoryCell ){
       NSIndexPath indexPath =  la.indexPath //only this indexPath can be used during the call to layoutAttributesForItemAtIndexPath:!!! 
       //extracts custom layout data from a layouts dictionary
       MyLayoutAttributes *cellLayout = layouts[la.indexPath];  
       //sets additional properties
        la.this = cellLayout.this
        la.that = cellLayout.that
        ...
    }
   ....
return array;
}

// layoutAttributesForItemAtIndexPath:

//layoutAttributesForItemAtIndexPath:

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {

  MyLayoutAttributes *la = (MyLayoutAttributes *)[super layoutAttributesForItemAtIndexPath:indexPath ];

    if(la.representedElementCategory == UICollectionElementCategoryCell ){
        NSIndexPath indexPath =  la.indexPath //only this indexPath can be used during the call !!! 
       //extracts custom layout data from a layouts dictionary using indexPath as a key
       MyLayoutAttributes *cellLayout = layouts[la.indexPath];  
       //sets additional properties
        la.this = cellLayout.this
        la.that = cellLayout.that
    }
return la;
}

这篇关于在滚动和重新加载时,UICollectionViewFlowLayout子类崩溃访问超出边界的数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆