2

Привет: Я использую представление коллекции в своем приложении, и я заметил, что для обновления требуется больше времени, чем ожидалось, используя reloadData. Моя коллекция имеет 1 section, и я тестирую ее с помощью 5 cell s (каждая из которых имеет 2 кнопки и ярлык). Я поместил несколько журналов в свой код, чтобы показать, сколько времени система действительно принимает для обновления. Интересно, что журналы показывают, что он освежает быстрее, чем он. На устройстве, например, он будет принимать до ~ 0.2sec (заметно), но вот журналы:UICollectionView reloadData кажется медленным/отстающим (не мгновенным)

0.007s Со времени reloadData называется ко времени cellForItemAtIndexPath называется в первый раз

0.002s за ячейку для загрузки и быть возвращены

0.041s Со времени reloadData называется ко времени, когда клетка # 5 возвращается

Существует нет ничего особенно интенсивно в cellForItemAtIndexPath функции (Basi cally просто находит словарь с 3 значениями в пределах NSArray по адресу indexPathrow). Даже когда я удалил это и просто вернул ячейку с пустой кнопкой, я увидел такое же поведение.

Есть ли у кого-нибудь идеи, почему это может произойти? Это происходит только на физическом устройстве (iPad Air). Благодаря!

EDIT # 1

Per @ комментарий Брайен-никелевые, я использовал инструмент Time Profiler, и обнаружили, что это действительно шип каждый раз reloadData называется. Вот скриншот:

Time Profiler

@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]; 

И мои 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; 

Когда инициализируется представление коллекции, ячейки регистрируются с использованием:

[moveUnitsView.collectionV registerNib:[UINib nibWithNibName:@"UnitSelectionCell" bundle:nil] forCellWithReuseIdentifier:@"UnitSelectionCell"]; 

EDIT # 2

@roycable и @ Aarón-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; 

Как ни странно, это не похоже, чтобы исправить эту проблему. Это буквально не делает интенсивных задач в этой функции, но все еще кажется отстающим (и Time Profiler, похоже, подтверждает это).

В ответ на просьбы о предоставлении кода из Universal, вместо размещения кода я просто резюмировать:

+units просто возвращает 17

+unitWithTag: использует switch, чтобы возвращать NSString соответствующее число между

+allianceExistsBetweenTeam: видит, если массив содержит один из строк

+setFontForSubviewsOfView: рекурсивная функция, которая в основном использует this code

К сожалению, это не представляется весьма актуальной, поскольку данный вопрос все еще происходит с упрощенным cellForItemAtIndexPath функции.

Я также внедрил новые предложения @ aaron-brager. Я удалил target перед добавлением нового, и я внес изменения в Time Profiler. Я не видел ничего действительно выскочить ... Вот скриншот. Все, что связано с UIImage не имеет никакого отношения к этому вопросу, как NSKeyedArchiver, так что только другие вещи, которые действительно имеют смысл являются строки, массивы и словари:

Time Profiler 2

Любое помощь очень ценится - Я на самом деле нужно получить это исправление (отсюда и щедрость). Спасибо!

Edit # 3 - Решение идентифицированного

Таким образом, получается, что этот вопрос не был ни в одном из этих функций. Проблема была в функции (назовем ее Function A), которая назвала функцию обновления выше (назовем ее Function B). Сразу после Function A, получившего название Function B, он выполнял задачу с интенсивным использованием ЦП. Я не знал о том, что reloadData - at least partially asynchronous, поэтому я беру на себя задачу с интенсивным процессором, а reloadData закончил гонку за процессорное время. Я решил свою проблему, добавив следующее прямо перед return cell;:

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

     [self performSelector:@selector(performMyCPUIntensiveTask:) withObject:myObject afterDelay:0.1]; 
    } 

Я надеюсь, что это помогает кто-то еще в будущем. Спасибо всем, кто помог, я искренне ценю это.

+0

Используете ли вы [Time Profiler] (https://developer.apple.com/Library/ios/documentation/AnalysisTools/Reference/Instruments_User_Reference/TimeProfilerInstrument/TimeProfilerInstrument.html)? Если вы испытываете задержку в 200 мс, после перезагрузки вы должны увидеть хороший большой всплеск. –

+0

Не могли бы вы предоставить нам весь ваш образец кода? Кроме того, вы используете многоразовые ячейки? – ArtSabintsev

+0

Спасибо за ваши ответы, ребята. Я добавил информацию в ответ на оба ваших комментария - см. Мое редактирование. – rebello95

ответ

3

Некоторые возможности:

  • Ваш предположительно рекурсивная функция для установки шрифтов, вероятно, дорого.
  • Некоторые из других функций Universal выглядят так, как будто они могут быть дорогими.
  • Кажется, что вы никогда не удаляете цель кнопки и каждый раз, когда ячейка повторно используется, вы добавляете к ней дополнительные цели.
  • imageWithContentsOfFile: пропускает кеш; вместо этого используйте imageNamed:.
+0

+1, принято и + щедрость (после того, как это позволяет мне) для всей вашей помощи, хотя источником проблемы была моя глупость в другой функции. Спасибо! – rebello95

10

Убедитесь, что вы в основной теме, когда звоните reloadData.

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

Если вы не используйте НОД, чтобы отправить сообщение на главной теме:

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

(не 100% уверен, что синтаксис, я просто напечатал это в браузере)

+0

Просто подтвердил, что это действительно работает * на * основной поток. Спасибо хоть. – rebello95

+1

Для меня в Swift прошло несколько секунд, чтобы вызвать reloadData, пока обновление коллекции не будет обновлено. Это решило это. – t0PPy

+0

Это тоже решает мою проблему. – RainCast