Ссылки на блоки как переменные экземпляра в Objective-C

Мне было интересно, можно ли сохранить ссылку на анонимную функцию (блок) в качестве переменной экземпляра в Objective-C.

Я знаю, как использовать делегирование, целевое действие и т. д. Я не об этом.


person Jacob Relkin    schedule 12.07.2010    source источник


Ответы (2)


Конечно.

typedef void(^MyCustomBlockType)(void);

@interface MyCustomObject {
  MyCustomBlockType block;
}
@property (nonatomic, copy) MyCustomBlockType block; //note: this has to be copy, not retain
- (void) executeBlock;
@end

@implementation MyCustomObject
@synthesize block;

- (void) executeBlock {
  if (block != nil) {
    block();
  }
}

- (void) dealloc {
  [block release];
  [super dealloc];
}
@end

//elsewhere:

MyCustomObject * object = [[MyCustomObject alloc] init];
[object setBlock:^{
  NSLog(@"hello, world!");
}];

[object executeBlock];
[object release];
person Dave DeLong    schedule 12.07.2010
comment
есть ли способ иметь общий тип для блока? Я ненавижу, что мне нужно иметь typedef там. - person Jacob Relkin; 12.07.2010
comment
@Jacob да, вы можете сделать: void(^)(void) (или любую другую подпись блока, которая вам нужна) везде, где вы видите MyCustomBlockType. Тем не менее, я обещаю, что использование typedef значительно облегчит грок в долгосрочной перспективе. Однако невозможно указать любой блок с любым возвращаемым значением и любыми параметрами. Вы должны четко указать подпись. - person Dave DeLong; 12.07.2010
comment
@Jacob Вы все еще ограничены системой типов C. Не существует такой вещи, как универсальный блок, хотя в C++ вы можете делать довольно умопомрачительные вещи с помощью шаблонов и блоков. Более реалистично, что тип id является общим по отношению к типам Objective-C, поэтому получение и возврат (возможно, nil) id дает вам большую гибкость. - person Barry Wark; 12.07.2010
comment
Зачем копировать, а не сильно? Даже если я использую ARC? - person ma11hew28; 09.01.2012
comment
@MattDiPasquale ответил 12 июля 2010, в 16:46 означает, что этот ответ предшествует ARC;) - person Dave DeLong; 09.01.2012
comment
Почему релиз после звонка на executeBlock? Вы бы хотели сделать это только в том случае, если бы были абсолютно уверены, что больше не будете звонить, не так ли? Или этот вызов необходим для освобождения захваченных переменных в блоке стека? - person Brent Dillingham; 09.01.2012
comment
@BrentDillingham смысл был в том, чтобы проиллюстрировать, как использовать блок в качестве ивара. Выделение, выполнение и немедленный выпуск вспомогательного объекта просто обеспечивают некоторый контекст того, как это можно использовать. - person Dave DeLong; 09.01.2012
comment
@DaveDeLong Спасибо - теперь я понимаю, что ошибочно думал, что вы освобождаете сам блок, а не объект. Я как всегда слишком быстро прочитал. - person Brent Dillingham; 28.02.2012
comment
Просто чтобы ответить на ЯВНЫЙ вопрос Джейкоба: @property (nonatomic, copy) void (^userObject)(); - person David H; 04.05.2012

Да, вы наверняка можете хранить ссылку на (копию) блока Objective-C. Объявление переменных немного запутано, как указатели на функции C, но в остальном это не проблема. Для блока, который принимает и id и возвращает void:

typedef void (^MyActionBlockType)(id);

@interface MyClass : NSObject 
{

}

@property (readwrite,nonatomic,copy) MyActionBlockType myActionBlock;
@end

будет делать свое дело.

person Barry Wark    schedule 12.07.2010
comment
Определение определенно волосатое. Я намного предпочитаю использовать typedef просто if. Тогда можно делать безумные вещи блоками, которые принимают блок в качестве параметра и возвращают блок и не теряются в скобках. :) - person Dave DeLong; 12.07.2010
comment
это typedef void (^MyActionBlockType)(id); правильно? разве не должно быть typedef void (^MyActionBlockType)(id obj); - person Duck; 25.03.2014
comment
@RubberDuck, имя переменной (obj) не имеет отношения к типу, поэтому вам не нужно объявлять его в typedef. Имя имеет значение только при реализации блока; - person Teemu Kurppa; 30.03.2014