Содержание
«О блоках — так о блоках» — подумал я, читая комментарий @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)
Очень похоже на объявление переменной, содержащей блок, но без ее имени после занчка ^.
(тип_возвращаемого_значения (^)(аргументы))имя_аргумента
На этом я заканчиваю, но это не все, что можно написать о блоках. Постараюсь как можно быстрее написать вторую часть.
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: