Objective-C enumerateUsingBlock против быстрого перечисления?
Каковы преимущества и недостатки следующих двух подходов:
enumerateUsingBlock
NSArray *myArray = [[NSArray alloc] init];
[myArray enumerateObjectsUsingBlock:^(id anObject, NSUInteger idx, BOOL *stop) {
if (anObject == someOtherObject) {
[anObject doSomething:idx];
*stop = YES;
}
}];
быстрое перечисление
NSArray *myArray = [[NSArray alloc] init];
int idx = 0
for (id anObject in myArray) {
if (anObject == someOtherObject) {
[anObject doSomething:idx];
break;
}
++idx;
}
2 ответа
Этот пост охватывает основные различия. В итоге:
- Быстрое перечисление доступно в OS X 10.5+, блоки доступны в 10.6+
- Для простого перечисления быстрое перечисление немного быстрее, чем основанное на блоках перечисление
- С одновременным или обратным перечислением легче (и более эффективно выполнять) перечисление на основе блоков, чем с быстрым перечислением
- При перечислении более
NSDictionary
Вы можете получить ключ и значение одним ударом с помощью перечислителя на основе блоков, тогда как при быстром перечислении вы должны использовать ключ, чтобы получить значение в отдельной отправке сообщения.
Что касается последней точки (перечисление NSDictionary), сравните это:
for (id key in dictionary)
{
id obj = [dictionary objectForKey: key];
// do something with key and obj
}
к этому:
[dictionary enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop) {
// do something with key and obj
}];
Кроме того, оба метода защищают изменяемые коллекции от мутаций внутри цикла перечисления. Интересно, что если вы попытаетесь изменить коллекцию внутри перечисления на основе блоков, вы получите исключение, выдаваемое CoreFoundation __NSFastEnumerationMutationHandler
, предполагая, что под капотом есть какой-то общий код.
NSMutableArray *myArray = [NSMutableArray arrayWithObjects:@"a", @"b", nil];
[myArray enumerateObjectsUsingBlock:^(id anObject, NSUInteger idx, BOOL *stop) {
// Attempt to mutate the array during enumeration
[myArray addObject:@"c"];
}];
Выход:
2011-12-14 22:37:53.716 Untitled[5809:707] *** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x109614190> was mutated while being enumerated.'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff8cca7286 __exceptionPreprocess + 198
1 libobjc.A.dylib 0x00007fff8319ad5e objc_exception_throw + 43
2 CoreFoundation 0x00007fff8cd311dc __NSFastEnumerationMutationHandler + 172
3 CoreFoundation 0x00007fff8cc9efb4 __NSArrayEnumerate + 612
4 Untitled 0x00000001094efcea main + 250
5 Untitled 0x00000001094efbe4 start + 52
6 ??? 0x0000000000000001 0x0 + 1
)
terminate called throwing an exceptionRun Command: line 1: 5809 Abort trap: 6 ./"$2"
Первые мысли, которые приходят в голову
Блоки доступны в iOS 4 и более поздних версиях, поэтому, если вам нужно поддерживать более старые версии, вы не можете использовать синтаксис блока.
Они довольно эквивалентны с точки зрения того, что они делают, кроме того, что вы не можете случайно испортить счетчик в блочной версии.
Еще одно различие в потенциале состоит в том, что вы можете определить блок в другом месте и передать в разных блоках в зависимости от вашего состояния.
Надеюсь, это был очень грубый пример, так как фрагмент кода довольно скудный, и есть более эффективный способ сделать это;)