Это неэффективный способ использования быстрого перечисления?
Я не совсем понимаю детали того, как работает быстрое перечисление, но сравниваю следующие два случая:
for(NSObject *object in self.myParent.parentsParents.granfathersMother.cousin.unclesNephew.array) {
// do something
}
против
NSArray *array = self.myParent.parentsParents.granfathersMother.cousin.unclesNephew.array;
for(NSObject *object in array) {
// do something
}
В первом примере будет ли он проходить через всю эту цепочку каждую итерацию, чтобы получить массив? Должен ли я использовать второй способ?
2 ответа
Я был на WWDC, когда Apple представила Fast Enumeration, и (насколько я помню) нам тогда сказали, что правый объект перемещается в темп. Кроме того, так должно быть, так как это работает:
for (идентификатор foo в [myCollection reverseObjectEnumerator])
Вы можете видеть, что коллекции, которые выполняют быстрое перечисление, принимают "Протокол быстрого перечисления" (NSFastEnumeration), который имеет один метод:
– countByEnumeratingWithState:objects:count:
Этот метод возвращает массив объектов C, который позволяет перечислению идти очень быстро, снова поддерживая одноразовое использование правой стороны.
Теперь, сказав все это, в настоящее время Apple советует разработчикам (в WWDC) использовать перечисление блоков, которое, как они утверждают, и быстрее, и генерирует меньше кода:
[myCollection enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
... your code
} ];
То, что мне нравится делать, это не использовать "id obj", а фактический тип (чтобы избежать приведения в блоке):
[myCollection enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop)
{
... your code
} ];
Ни компилятор, ни анализатор (4.4) не жалуются, когда я делаю это.
Если вам нужно установить переменную вне этого метода, то вы должны сделать ее блочной переменной:
__block int foo = 0;
[myCollection enumerateObjectsUsingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop)
{
foo = MAX(foo, [num integerValue]);
} ];
РЕДАКТИРОВАТЬ: в качестве пояснения, прямой ответ на ваш вопрос "нет", утверждение "self.myParent.parentsParents.granfathersMother.cousin.unclesNephew.array" оценивается один раз, а конечный объект сохраняется как временная переменная в стеке. Также вы можете использовать ту же технику с перечислениями блоков - оператор вычисляется один раз, а последний возвращаемый объект используется для перечисления.
__block int foo = 0;
[self.myParent.parentsParents.granfathersMother.cousin.unclesNephew.array enumerateObjectsUsingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop)
{
foo = MAX(foo, [num integerValue]);
} ];
РЕДАКТИРОВАТЬ 2: Я нашел другую тему на SO, где обсуждалась эта же тема. Единственное, что я упустил в отношении перечисления блоков, это то, что вы можете указать, что они должны выполняться одновременно (или наоборот), используя немного более сложный метод:
enumerateObjectsWithOptions:usingBlock:
Поскольку устройства iOS получают все больше и больше ядер, это может быть большой победой в зависимости от того, что вы делаете.
Ответ @bbum на этот вопрос (и другие тоже) здесь.
Это, вероятно, зависит от компилятора (т.е. не определено). Если вас это беспокоит, то добавьте некоторый временной код и узнайте сами:
#import <sys/time.h>
static unsigned getTickCount()
{
struct timeval tv;
gettimeofday(&tv, 0);
return (unsigned)((tv.tv_sec * 1000) + (tv.tv_usec / 1000));
}
...
unsigned startTime = getTickCount();
for(NSObject *object in self.myParent.parentsParents.granfathersMother.cousin.unclesNephew.array) {
// do something
}
unsigned endTime = getTickCount();
NSLog(@"That took %umS", endTime - startTime);
Вам нужно будет иметь довольно большой массив, чтобы зарегистрировать что-либо выше 0
,