Оптимизация для цикла в Objective-C

Просто найдите несколько простых советов о том, как лучше оптимизировать цикл for (с точки зрения использования памяти) в Obj-C и ARC, принимая этот неоптимизированный произвольный код как:

NSMutableArray *array = [NSMutableArray array];
for (NSDictionary *dict in NSArray *arrayOfDicts) {
 NSString *first = [dict valueForKeyPath:@"object.first"];
 NSString *second = [dict valueForKeyPath:@"object.second"];
 NSString *third = [dict valueForKeyPath:@"object.third"];
 [array addObject:@[first, second, third]];
}

Какой из них лучше/прав на практике (при условии, что многие циклы могут иметь значение)

1) Объявите один раз, скопируйте в массив.

NSMutableArray *array = [NSMutableArray array];
NSString *first, *second, *third;
for (NSDictionary *dict in NSArray *arrayOfDicts) {
 first = [dict valueForKeyPath:@"object.first"];
 second = [dict valueForKeyPath:@"object.second"];
 third = [dict valueForKeyPath:@"object.third"];
 [array addObject:@[[first copy], [second copy], [third copy]]];
 first, second, third = nil;
}

2) Autoreleasepool

NSMutableArray *array = [NSMutableArray array];
for (NSDictionary *dict in NSArray *arrayOfDicts) {
 @autoreleasepool {
 NSString *first = [dict valueForKeyPath:@"object.first"];
 NSString *second = [dict valueForKeyPath:@"object.second"];
 NSString *third = [dict valueForKeyPath:@"object.third"];
 [array addObject:@[first, second, third]];
 }
}

3) Что-то еще?

2 ответа

first, second и third выходят за рамки цикла во всех ситуациях независимо от того, что они находятся в dict. Массив @[first, second, third] сохраняется независимо от того, что он добавлен в массив.

Следовательно, будет очень ограниченная разница между тремя. valueForKeyPath: не создает копию, поэтому все, о чем вы говорите, это (i) хранение в таблице count count *; и (ii) хранение в пуле авторесурсов.

(i) не просто пренебрежимо мала, но настолько внутренна к текущей реализации среды выполнения, что нет смысла полагаться на нее. (ii) также пренебрежимо мала, но явно требуется спецификацией.

Технически @autoreleasepool, вероятно, будет немного более компактным (в зависимости от общего размера пустого пула и размера, необходимого для добавления объекта в пул по сравнению с длиной вашего массива), но я бы не стал его одерживать.

Абсолютно никакой разницы между двумя первыми вариантами. Как локальное хранилище не влияет на ARC - даже не полагаясь на точные детали реализации ARC, вы переназначаете в верхней части следующей итерации независимо.

(* keep counts не сохраняются до тех пор, пока они больше 1, поскольку 1 является очень распространенным значением и подразумевается объектом, существующим вообще, поэтому они идут в отдельной таблице, а не с объектом)

2018 edit: в 64-битной среде выполнения хранится счетчик хранения больше 1, но меньше, чем очень большое число в части указателя isa, поскольку в настоящее время не требуется весь 64-разрядный диапазон. И если это когда-либо, они могут просто переместить счет сохранения снова. Очень большое число было 2 ^ 19 + 1 в исходной 64-битной среде исполнения, но с небольшим размером, возможно, изменилось с тех пор. Таким образом, дополнительное сохранение существенно не увеличивает объем памяти на современных устройствах.


Самое большое улучшение скорости - это удаление бесплатных вызовов valueForKeyPath.

valueForKeyPath задан строковый аргумент, например object.first. Затем он должен проанализировать строку: найдите точку посередине, выясните, что приемник - это словарь, что "объект" не начинается с символа @, поэтому он соответствует objectForKey и т.д. Все это три раза. Вместо

for (NSDictionary *dict in NSArray *arrayOfDicts) {
 SomeObject* someObject = [dict objectForKey:@"object"];
 [array addObject:@[[someObject.first copy], 
 [someObject.second copy],
 [someObject.third copy]]];
}

Вам решать, нужна ли копия. Объекты часто имеют свойства "копировать", так что сначала, во-вторых, третье может быть копиями.

licensed under cc by-sa 3.0 with attribution.