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

reb*_*o95 4 objective-c reloaddata ios uicollectionview uicollectionviewcell

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

0.007sreloadData调用的时间到第一次cellForItemAtIndexPath调用的时间

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

0.041sreloadData调用时间到返回单元格#5的时间

cellForItemAtIndexPath函数中没有任何特别密集的东西(基本上只是NSArrayindexPaths中找到一个包含3个值的字典row).即使我删除了这个并且只返回一个带有空白按钮的单元格,我也看到了相同的行为.

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

编辑#1

根据Per @ brian-nickel的评论,我使用了Time Profiler工具,并发现每次reloadData调用确实都会出现峰值.这是一个截图:

时间分析器

@ArtSabintsev,这是围绕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];
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

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

[moveUnitsView.collectionV registerNib:[UINib nibWithNibName:@"UnitSelectionCell" bundle:nil] forCellWithReuseIdentifier:@"UnitSelectionCell"];
Run Code Online (Sandbox Code Playgroud)

编辑#2

@roycable和@ aaron-brager指出这可能是由于使用造成的imageWithContentsOfFile:.为了测试这一点,我改为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];

    [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;
Run Code Online (Sandbox Code Playgroud)

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

响应代码请求Universal,而不是发布代码,我只是总结一下:

+units 回来 17

+unitWithTag:使用a switch来返回NSString对应的数字1-17

+allianceExistsBetweenTeam: 查看数组是否包含其中一个字符串

+setFontForSubviewsOfView:是一个基本上使用此代码的递归函数

不幸的是,这似乎不太相关,因为过度简化的cellForItemAtIndexPath功能仍然会出现问题.

我还实施了@ aaron-brager的新建议.我target在添加新的之前删除了它,然后我对Time Profiler进行了更改.我没有看到任何真正的弹出...这是截图.与UIImage此问题相关的所有内容都与此问题无关NSKeyedArchiver,因此唯一真正有意义的事情是字符串,数组和字典:

时间分析器2

非常感谢任何和所有的帮助 - 我真的需要得到这个固定(因此赏金).谢谢!

编辑#3 - 确定解决方案

因此,事实证明问题不在于这些功能中的任何一个.问题是Function A上面调用更新函数的函数(让我们称之为Function B)(让我们称之为).在Function A调用之后Function B,它执行了CPU密集型任务.我不知道的事实reloadData至少部分异步,所以我假设的CPU密集型任务,reloadData结束了比赛的CPU时间.我之前添加以下内容解决了我的问题return cell;:

    if (indexPath.row == [self collectionView:collectionView numberOfItemsInSection:indexPath.section] - 1) {

        [self performSelector:@selector(performMyCPUIntensiveTask:) withObject:myObject afterDelay:0.1];
    }
Run Code Online (Sandbox Code Playgroud)

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

Tho*_*ler 10

打电话时确保你在主线程上reloadData.

NSLog("on main thread: %@", [NSThread isMainThread] ? @"YES" : @"NO");
Run Code Online (Sandbox Code Playgroud)

如果你不是那么使用GCD在主线程上发送消息:

dispatch_async(dispatch_get_main_queue(), ^{
    [moveUnitsView.collectionV reloadData];
});
Run Code Online (Sandbox Code Playgroud)

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


Aar*_*ger 5

一些可能性:

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