Блоки в Objective-C

«О блоках — так о блоках» — подумал я, читая комментарий @slashik к этой статье. Постараюсь описать все максимально просто. Для начала скажу, что блоки — это в некотором виде указатели на функцию.

Для начала посмотрим, где мы используем блоки? Один из явных примеров — перебор NSArray:

NSArray* array = ...;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    // обрабатываем отдельный объект obj
}];

Вот оно явное использование блока! Что тут является блоком? Вот:

^(id obj, NSUInteger idx, BOOL *stop) {
    // обрабатываем отдельный объект obj
}

Как вы уже догадались

(id obj, NSUInteger idx, BOOL *stop)

ничто иное, как объявление аргументов. Конкретно этот блок не возвращает ничего, но, конечно-же, блоки могут и возвращать что-нибудь. Вот пример блока, возвращающего результат сравнения двух объектов:

[array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    return NSOrderedSame;
}];

Сам блок:

^NSComparisonResult(id obj1, id obj2) {
    return NSOrderedSame;
}

Что же отличает два приведенных блока? Ответ прост: у блока, который возвращает что-либо между знаком ^ и открывающей скобкой располагается тип возвращаемого значения.

Как создать блок

Немного обобщим:

^тип_возвращаемого_значения(аргументы){
    код блока
}

Если блок ничего не возвращает, либо нету входных аргументов — эту часть можно опустить. Примеры блоков:

Блок, который возвращает строку и принимает на вход две строки:

^NSString*(NSString* a, NSString* b) {
    return [a stringByAppendingString:b];
};

Блок, который просто возвращает строку и не принимает аргументов:

^NSString*() {
    return @"Hello, World!";
};

А теперь тот же блок, но мы опустим объявление аргументов:

^NSString* {
    return @"Hello, World!";
};

Мы привыкли к тому, что если функция (метод) ничего не возвращает — мы пишем void. В блоке, который ничего не возвращает это тоже можно делать:

^void {
    // блок который ничего не возвращает
};

Но и это мы можем опустить. Итак, блок, который не принимает аргументов и ничего не возвращает:

^{
    // код блока
}

С созданием блока, я думаю, все понятно. Теперь поговорим о …

Как объявлять переменные которые хранят блок

Очевидно, что переменная, хранящая блок должна описывать его аргументы и тип возвращаемого значения. Начнем с блоков, которые ничего не возвращают и ничего не принимают на вход:

void (^block)() = ^{
    NSLog(@"Hello, world");
};

Обобщим синтаксис:

тип_возвращаемого_значения (^имя_переменной)(аргументы) = ^тип_возвращаемого_значения(аргументы){
    // код блока
};

Несколько примеров. Блок который возвращает NSString*:

NSString* (^myBlock)() = ^NSString*{
    return @"Hello, World!";
};

Блок, который ничего не возвращает, но принимает аргументы на вход:

void (^block)(NSString*, NSString*) = ^(NSString* a, NSString* b){
    NSLog(@"%@ - %@", a, b);
};

Блок, который принимает аргументы на вход и возвращает значение:

NSString*(^block)(NSString*,NSString*) = ^NSString*(NSString* a, NSString* b) {
    return [a stringByAppendingString:b];
};

Вызов блоков

Ну вот мы научились создавать блоки и хранить их в переменных. Что нам с этим делать? Правильно! Вызывать на выполнение. Блок вызывается как обычная функция:

NSString*(^block)(NSString*,NSString*) = ^NSString*(NSString* a, NSString* b) {
    return [a stringByAppendingString:b];
};

NSString* myString = block(@"hello", @"world");

В myString будет результат работы блока.

Передача блоков в метод

Блоки не были бы таким классным средством, если бы их нельзя было бы передавать в другие методы. Пример такой передачи приведен в начале статьи (перебор массива). Именно это сейчас и рассмотрим.

Представим, что у нас есть метод, который разбивает строку на компоненты пробелом, а затем вызывает блок-обработчик и передает ему каждый отдельный компонент.

-(void) separateStringBySpace:(NSString*)string processWithBlock:(void(^)(NSString* s))block
{
    // разбиваем строку на компоненты
    NSArray* components = [string componentsSeparatedByString:@" "];

// для каждого компонента вызываем блок for (NSString* s in components) block(s); }

Пользоваться будем так:

[self separateStringBySpace:@"hello world this is idev.by" processWithBlock:^(NSString *s) {
    // сюда нам будет приходить каждый отдельный компонент в переменной s
}];

Как видим, мы передали код (блок), которым должен обрабатываться отдельный компонент и вызвали его внутри метода separateStringBySpace:processWithBlock: .

Рассмотрим подробнее объявление метода:

-(void) separateStringBySpace:(NSString*)string processWithBlock:(void(^)(NSString* s))block

А точнее — объявление аргумента, который будет нести в себе блок:

(void(^)(NSString* s))block

Тип объявляется в скопках, как и все типы аргументов, входящих в метод. Имя аргумента справа от скобок. Внутри скобок:

void(^)(NSString* s)

Очень похоже на объявление переменной, содержащей блок, но без ее имени после занчка ^.

(тип_возвращаемого_значения (^)(аргументы))имя_аргумента

На этом я заканчиваю, но это не все, что можно написать о блоках. Постараюсь как можно быстрее написать вторую часть.

Рейтинг
( Пока оценок нет )
webnewsite.ru / автор статьи
Загрузка ...

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: