В этой части мы узнаем какие типы блоков существуют. Предлагаю это сделать опытным путем. Будем считать, что блоки — это объекты каких-то классов. Итак, поехали!
Для начала, самый простой блок:
void(^block)() = ^{ }; NSLog(@"%@", [block class]);
Так как блоки — это объекты классов, то вызов [block class] можно использовать. Результат выполнения кода:
2012-06-03 15:00:38.015 BlockApp[53173:f803] __NSGlobalBlock__
Ура, один класс определили. Попробуем немного изменить блок.
int a = 5; void(^block)() = ^{ NSLog(@"%d",a); }; NSLog(@"%@", [block class]);
Результат:
2012-06-03 15:04:48.938 BlockApp[53328:f803] __NSStackBlock__
Отлично! Еще один класс. Теперь сделаем вот так:
NSString* string = [NSString stringWithFormat:@"Hello, World!"]; void(^block)() = ^{ NSLog(@"%@",string); };block = [[block copy] autorelease];
NSLog(@"%@", [block class]);
Вот что получим:
2012-06-03 15:21:32.648 BTable[53570:f803] __NSMallocBlock__
Итак, у нас есть три типа блоков. Что значит каждый из типов?
Если блок не ссылается на внешние переменные — он получает тип __NSGlobalBlock__. Время его жизни ограничено временем жизни приложения.
Если блок ссылается на какие-либо внешние переменные, но не копируется (вызовом copy) он получает тип __NSStackBlock__ и создается на стеке.
Также, блок может «жить» и в куче, это — __NSMallocBlock__. Как вы заметили, для этого нужно вызывать copy у блока. Следует обратить внимание на то, что retain для внешних объектов (по отношению к блоку) вызывается не при инициализации блока, а при копировании блока в кучу.
Рассмотрим пример, в котором мы откладываем вызов блока. Создадим таймер на 1 секунду.
NSDate* date = [NSDate date]; void(^block)() = ^{ NSLog(@"%@",date); };[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTick:) userInfo:block repeats:NO];
И метод срабатывания таймера:
-(void) timerTick:(NSTimer *)timer { void(^block)() = [timer userInfo]; block(); }
Будет создан __NSStackBlock__ и этот код «положит» наше приложение. Так как к моменту вызова блок уже не будет существовать. Попробуем так:
NSDate* date = [NSDate date]; void(^block)() = ^{ NSLog(@"%@",date); }; // скопируем блок в кучу block = [[block copy] autorelease]; [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTick:) userInfo:block repeats:NO];
Метод срабатывания таймера остался таким же. После копирования, блок поместиться в кучу и станет __NSMallocBlock__. Код отработает успешно и не положит наше приложение.
А что же с __NSGlobalBlock__ ? Пробуем:
void(^block)() = ^{ NSLog(@"Hello, world!"); };[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTick:) userInfo:block repeats:YES];
Блок не ссылается на внешние переменные и является __NSGlobalBlock__. Этот код не свалит приложение, так как время жизни __NSGlobalBlock__ ограничено только временем жизни приложения.
Из этого всего можно сделать вывод — если нужно сделать отложенный вызов блока — его нужно копировать в кучу.
На этом все, спасибо за внимание!
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: