Используя блоки
Вызов блока
Если Вы объявляете блок как переменную, можно использовать его, поскольку Вы были бы функция, как показано в этих двух примерах:
int (^oneFrom)(int) = ^(int anInt) { |
return anInt - 1; |
}; |
printf("1 from 10 is %d", oneFrom(10)); |
// Prints "1 from 10 is 9" |
float (^distanceTraveled)(float, float, float) = |
^(float startingSpeed, float acceleration, float time) { |
float distance = (startingSpeed * time) + (0.5 * acceleration * time * time); |
return distance; |
}; |
float howFar = distanceTraveled(0.0, 9.8, 1.0); |
// howFar = 4.9 |
Часто, однако, Вы передаете блок как параметр функции или методу. В этих случаях Вы обычно создаете «встроенный» блок.
Используя блок как аргумент функции
Можно передать блок как аргумент функции так же, как Вы были бы любой другой параметр. Во многих случаях, однако, Вы не должны объявлять блоки; вместо этого Вы просто реализуете их встроенный, где они требуются как параметр. Следующий пример использует qsort_b
функция. qsort_b
подобно стандарту qsort_r
функция, но берет блок в качестве его заключительного параметра.
char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" }; |
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) { |
char *left = *(char **)l; |
char *right = *(char **)r; |
return strncmp(left, right, 1); |
}); |
// Block implementation ends at "}" |
// myCharacters is now { "Charles Condomine", "George", "TomJohn" } |
Заметьте, что блок содержится в списке аргументов функции.
Следующий пример показывает, как использовать блок с dispatch_apply
функция. dispatch_apply
объявляется следующим образом:
void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t)); |
Функция представляет блок очереди отгрузки для многократных вызовов. Требуется три параметра; первое указывает число итераций для выполнения; второе указывает очередь, которой представлен блок; и третьим является сам блок, поочередно берущий отдельный аргумент — текущий индекс итерации.
Можно использовать dispatch_apply
тривиально только распечатать итеративный индекс, как показано:
#include <dispatch/dispatch.h> |
size_t count = 10; |
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); |
dispatch_apply(count, queue, ^(size_t i) { |
printf("%u\n", i); |
}); |
Используя блок как аргумент метода
Какао обеспечивает много методов то использование блоки. Вы передаете блок как аргумент метода так же, как Вы были бы любой другой параметр.
Следующий пример определяет индексы любого из первых пяти элементов в массиве, появляющихся в данном наборе фильтра.
NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"]; |
NSSet *filterSet = [NSSet setWithObjects: @"A", @"Z", @"Q", nil]; |
BOOL (^test)(id obj, NSUInteger idx, BOOL *stop); |
test = ^(id obj, NSUInteger idx, BOOL *stop) { |
if (idx < 5) { |
if ([filterSet containsObject: obj]) { |
return YES; |
} |
} |
return NO; |
}; |
NSIndexSet *indexes = [array indexesOfObjectsPassingTest:test]; |
NSLog(@"indexes: %@", indexes); |
/* |
Output: |
indexes: <NSIndexSet: 0x10236f0>[number of indexes: 2 (in 2 ranges), indexes: (0 3)] |
*/ |
Следующий пример определяет ли NSSet
объект содержит слово, указанное локальной переменной, и устанавливает значение другой локальной переменной (found
) к YES
(и останавливает поиск), если он делает. Заметьте это found
также объявляется как a __block
переменная, и что блок определяется встроенный:
__block BOOL found = NO; |
NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil]; |
NSString *string = @"gamma"; |
[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) { |
if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) { |
*stop = YES; |
found = YES; |
} |
}]; |
// At this point, found == YES |
Копирование блоков
Как правило, Вы не должны должны быть копировать (или сохранять) блок. Только необходимо сделать копию, когда Вы ожидаете, что блок будет использоваться после уничтожения объема, в котором это было объявлено. Копирование перемещает блок в «кучу».
Можно скопировать и выпустить блоки с помощью C функции:
Block_copy(); |
Block_release(); |
Для предотвращения утечки памяти необходимо всегда балансировать a Block_copy()
с Block_release()
.
Образцы для предотвращения
Блочный литерал (т.е. ^{ ... }
) адрес локальной для штабеля структуры данных, представляющей блок. Объем локальной для штабеля структуры данных является поэтому составным оператором включения, таким образом, необходимо избежать образцов, показанных в следующих примерах:
void dontDoThis() { |
void (^blockArray[3])(void); // an array of 3 block references |
for (int i = 0; i < 3; ++i) { |
blockArray[i] = ^{ printf("hello, %d\n", i); }; |
// WRONG: The block literal scope is the "for" loop. |
} |
} |
void dontDoThisEither() { |
void (^block)(void); |
int i = random(): |
if (i > 1000) { |
block = ^{ printf("got i at: %d\n", i); }; |
// WRONG: The block literal scope is the "then" clause. |
} |
// ... |
} |
Отладка
Можно установить точки останова и единственный шаг в блоки. Можно вызвать блок из использования сеанса GDB invoke-block
, как проиллюстрировано в этом примере:
$ invoke-block myBlock 10 20 |
Если Вы хотите передать в струне до, необходимо заключить ее в кавычки. Например, для передачи this string
в doSomethingWithString
блок, Вы записали бы следующее:
$ invoke-block doSomethingWithString "\"this string\"" |