Используя блоки

Вызов блока

Если Вы объявляете блок как переменную, можно использовать его, поскольку Вы были бы функция, как показано в этих двух примерах:

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\""