UICollectionView reloadData似乎缓慢/滞后(不是瞬时的)

时间:2014-09-02 00:15:41

标签: ios objective-c uicollectionview uicollectionviewcell reloaddata

您好:我正在我的应用程序中使用集合视图,并且我注意到使用reloadData刷新所需的时间比预期的要长。我的集合视图有1 section,我用5 cell s(每个都有2个按钮和一个标签)测试它。我将一些日志放入我的代码中,以显示系统实际需要刷新多长时间。有趣的是,日志表明它的刷新速度比它快。例如,在设备上,它将需要约0.2秒(明显),但这是日志:

0.007s从调用reloadData到第一次调用时间cellForItemAtIndexPath

0.002s每个要加载并返回的单元格

0.041s从调用时间reloadData到返回单元格#5的时间

cellForItemAtIndexPath函数中没有任何特别密集的内容(基本上只是在NSArray indexPath的{​​{1}}内找到包含3个值的字典。 即使我删除了这个并且只返回了一个带有空白按钮的单元格,我也看到了相同的行为。

有没有人知道为什么会这样?顺便提一下,它只发生在物理设备(iPad Air)上。谢谢!

编辑#1

Per @ brian-nickel的评论,我使用了Time Profiler工具,发现每次调用row时确实会出现峰值。这是一个截图:

Time Profiler

@ArtSabintsev,这是围绕reloadData来电的功能,然后是reloadData

cellForItemAtIndexPath

我的//Arrays were just reset, load new data into them //Loop through each team for (NSString *team in moveUnitsView.teamsDisplaying) { //CURRENT TEAM WILL COME FIRST //Create an array for this team NSMutableArray *teamArr = [NSMutableArray new]; //Loop through all units for (int i = [Universal units]; i > 0; i--) { //Set the unit type to a string NSString *unitType = [Universal unitWithTag:i]; //Get counts depending on the team if ([team isEqualToString:currentTeam.text]) { //Get the number of units of this type so that it supports units on transports. If the territory is a sea territory and the current unit is a ground unit, check the units in the transports instead of normal units int unitCount = (ter.isSeaTerritory && (i == 1 || i == 2 || i == 8)) ? [self sumOfUnitsInTransportsOfType:unitType onTerritory:ter onTeam:team] : [ter sumOfUnitsOfType:unitType onTeam:team]; //Get the number of movable units on this territory int movableCount = 0; if (queue.selectedTerr != nil && queue.selectedTerr != ter) { //This is here to prevent the user from selecting units on another territory while moving units from one territory movableCount = 0; } else if (ter.isSeaTerritory && (i == 1 || i == 2 || i == 8)) { //Units on transports - can be an enemy territory movableCount = [self sumOfUnitsInTransportsOfType:unitType onTerritory:ter onTeam:team]; } else if ([Universal allianceExistsBetweenTeam:team andTeam:ter.currentOwner] || i == 3 || i == 9) { //Other units - only planes can be on an enemy territory movableCount = [ter sumOfMovableUnitsOfType:unitType onTeam:team]; } //See if there are units of this type on this territory on this team if (unitCount > 0) { //Add data to this team's dictionary NSMutableDictionary *unitInfo = [NSMutableDictionary new]; [unitInfo setObject:@(i) forKey:@"UnitTag"]; [unitInfo setObject:unitType forKey:@"UnitType"]; [unitInfo setObject:@(unitCount) forKey:@"Count"]; [unitInfo setObject:@(movableCount) forKey:@"MovableCount"]; [unitInfo setObject:team forKey:@"Team"]; //Add the dictionary [teamArr addObject:unitInfo]; //Increment the counter if (unitsOnCT) { //Must check or it could cause a crash *unitsOnCT += 1; } } } } //Add the team array [moveUnitsView.unitData addObject:teamArr]; } //Reload the data in the collection view [moveUnitsView.collectionV reloadData]; 相关代码:

cellForItemAtIndexPath

初始化集合视图时,使用以下命令注册单元格:

    //Dequeue a cell
    UnitSelectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"UnitSelectionCell" forIndexPath:indexPath];

    //Get the team array (at the index of the section), then the unit's data (at the index of the row)
    NSMutableDictionary *unitData = (moveUnitsView.unitData[indexPath.section])[indexPath.row];

    //Get values
    int unitTag = [[unitData objectForKey:@"UnitTag"] intValue];
    int count = [[unitData objectForKey:@"Count"] intValue];
    int movableCount = [[unitData objectForKey:@"MovableCount"] intValue];
    NSString *unitType = [unitData objectForKey:@"UnitType"];

    //Set the cell's values
    [cell.upB addTarget:self action:@selector(upMoveUnits:) forControlEvents:UIControlEventTouchUpInside]; [cell.upB setTag:unitTag];
    [cell.iconB setBackgroundImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[Universal imageNameForUnit:unitType team:[unitData objectForKey:@"Team"]] ofType:nil]] forState:UIControlStateNormal];
    [cell.iconB setTitle:[Universal strForExpDisplay:count] forState:UIControlStateNormal];
    [Universal adjustTitlePlacementOfB:cell.iconB autosize:FALSE]; //Don't autosize because this is a collection view
    cell.unitTypeL.text = unitType;
    cell.unitTypeL.adjustsFontSizeToFitWidth = cell.unitTypeL.adjustsLetterSpacingToFitWidth = TRUE;

    //Set fonts
    [Universal setFontForSubviewsOfView:cell];

    //Return the cell
    return cell;

编辑#2

@roycable和@ aaron-brager指出这可能是由使用[moveUnitsView.collectionV registerNib:[UINib nibWithNibName:@"UnitSelectionCell" bundle:nil] forCellWithReuseIdentifier:@"UnitSelectionCell"]; 引起的。为了测试这一点,我将imageWithContentsOfFile:更改为:

cellForItemAtIndexPath

奇怪的是,这似乎没有解决问题。它实际上并没有在该功能中执行密集任务,但它似乎仍然滞后(Time Profiler似乎证实了这一点)。

响应来自 //Dequeue a cell UnitSelectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"UnitSelectionCell" forIndexPath:indexPath]; //Get the team array (at the index of the section), then the unit's data (at the index of the row) NSMutableDictionary *unitData = (moveUnitsView.unitData[indexPath.section])[indexPath.row]; //Get values int unitTag = [[unitData objectForKey:@"UnitTag"] intValue]; [cell setBackgroundColor:[UIColor redColor]]; [cell.upB removeTarget:nil action:NULL forControlEvents:UIControlEventTouchUpInside]; [cell.upB addTarget:self action:@selector(upMoveUnits:) forControlEvents:UIControlEventTouchUpInside]; [cell.upB setTag:unitTag]; //Return the cell return cell; 的代码请求,而不是发布代码,我只是总结一下:

Universal只返回+units

17使用+unitWithTag:返回与switch之间的数字对应的NSString

1-17查看数组是否包含其中一个字符串

+allianceExistsBetweenTeam:是一个递归函数,基本上使用this code

不幸的是,这似乎不太相关,因为问题仍然出现在过度简化的+setFontForSubviewsOfView:函数中。

我还实施了@ aaron-brager的新建议。我在添加新版本之前删除了cellForItemAtIndexPath,然后对Time Profiler进行了更改。我没有看到任何真正的弹出...这是截图。与target相关的所有内容与此问题无关,就像UIImage一样,所以唯一真正有用的其他内容是字符串,数组和字典:

Time Profiler 2

非常感谢任何和所有帮助 - 我真的需要修复它(因此是赏金)。谢谢!

编辑#3 - 确定解决方案

因此,事实证明问题不在于这两个功能中的任何一个。问题是上面调用更新函数的函数(让我们称之为NSKeyedArchiver)(让我们称之为Function A)。在Function B调用Function A之后,它执行了一项CPU密集型任务。我不知道Function Bat least partially asynchronous的事实,所以我假设CPU密集型任务,reloadData最终争夺CPU时间。我在reloadData之前添加以下内容解决了我的问题:

return cell;

我希望将来可以帮助别人。感谢所有帮助过的人,我真诚地感谢他们。

2 个答案:

答案 0 :(得分:10)

请在致电reloadData时确保您在主线程上。

NSLog("on main thread: %@", [NSThread isMainThread] ? @"YES" : @"NO");

如果你没有,那么使用GCD在主线程上发送消息:

dispatch_async(dispatch_get_main_queue(), ^{
    [moveUnitsView.collectionV reloadData];
});

(不是100%确定语法,我只是在浏览器中输入了这个)

答案 1 :(得分:4)

一些可能性:

  • 设置字体的推测功能可能很昂贵。
  • 其他一些Universal函数看起来可能很贵。
  • 您似乎没有删除按钮目标,并且每次重复使用单元格时,您都会向其中添加其他目标。
  • imageWithContentsOfFile:跳过缓存;请改用imageNamed: