В этой части мы узнаем какие типы блоков существуют. Предлагаю это сделать опытным путем. Будем считать, что блоки — это объекты каких-то классов. Итак, поехали!

Для начала, самый простой блок:

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__ ограничено только временем жизни приложения.

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

На этом все, спасибо за внимание!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *