Может ли блок Obj-C выполняться сам по себе?

Это расширение этого вопроса: Можно ли создать категорию объекта Block в Objective-C.

По сути, хотя кажется возможным создать категорию для блоков либо через NSObject, либо через NSBlock, у меня возникают проблемы с пониманием того, как блок сможет оценивать себя. Пример, приведенный в ответе на последний вопрос:

- (void) doFoo {
  //do something awesome with self, a block
  //however, you can't do "self()".  
  //You'll have to cast it to a block-type variable and use that
}

Подразумевает, что можно каким-то образом привести себя к переменной блока, но как выполнить сам блок? Например, скажем, я сделал категорию на NSBlock и в методе сделал:

NSBlock* selfAsBlock = (NSBlock*)self;

Есть ли какое-либо сообщение, которое я могу отправить в selfAsBlock для оценки блока?


person donalbain    schedule 06.09.2011    source источник
comment
Да, ты можешь. Вы можете найти больше в этом (уже на SO) stackoverflow.com/questions/4824613/   -  person Joshua Smith    schedule 06.09.2011
comment
Я не понимаю, как это решает эту проблему. Этот ответ, похоже, сосредоточен на том, как вызвать блок из его собственного определения блока. Я говорю об оценке блока из самого блочного объекта. Чтобы быть немного более конкретным, я надеюсь достичь этого, чтобы иметь возможность добавлять методы к блокам (либо в NSObject, либо в NSBlock) для выполнения потока управления на основе блоков (т.е. [block whileTrueDo: block]). Для этого мне нужно, чтобы блок переоценил себя в методе.   -  person donalbain    schedule 06.09.2011


Ответы (2)


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

Нравится:

- (void)doFoo {
    // Assume the block receives an int, returns an int,
    // and cast self to the corresponding block type
    int (^selfBlock)(int) = (int (^)(int))self;

    // Call itself and print the return value
    printf("in doFoo: %d\n", selfBlock(42));
}

Обратите внимание, что (в большинстве случаев) вам необходимо исправить подпись блока, чтобы компилятор мог настроить сайт вызова в соответствии с ABI целевой платформы. В приведенном выше примере подпись имеет возвращаемый тип int, один параметр типа int.

Полный пример:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface Foo : NSObject
- (void)doFoo;
@end

@implementation Foo
- (void)doFoo {
    // Assume the block receives an int, returns an int,
    // and cast self to the corresponding block type
    int (^selfBlock)(int) = (int (^)(int))self;

    // Call itself and print the return value
    printf("in doFoo: %d\n", selfBlock(42));
}
@end

int main(void) {
    [NSAutoreleasePool new];

    // From Dave's answer
    Method m = class_getInstanceMethod([Foo class], @selector(doFoo));
    IMP doFoo = method_getImplementation(m);
    const char *type = method_getTypeEncoding(m);
    Class nsblock = NSClassFromString(@"NSBlock");
    class_addMethod(nsblock, @selector(doFoo), doFoo, type);

    // A block that receives an int, returns an int
    int (^doubler)(int) = ^int(int someNumber){ return someNumber + someNumber; };

    // Call the category method which in turn calls itself (the block)
    [doubler doFoo];

    return 0;
}
person Community    schedule 06.09.2011
comment
Здорово. Нужен ли явный динамический метод времени выполнения, чтобы добавить код? Или это тоже можно сделать через категорию? - person donalbain; 06.09.2011
comment
@don Как объясняется в ответах на вопрос, который вы связали, я не думаю, что можно использовать категорию, поскольку компилятору требуется исходное объявление интерфейса при анализе категории. - person ; 06.09.2011

NSBlock имеет метод invoke, который можно использовать для вызова блока.

NSBlock* b = ^() { /* do stuff */ };
[b invoke];

Обратите внимание, что это закрытый, недокументированный метод.

person 一二三    schedule 06.09.2011
comment
Именно такой метод я и искал. Конечно, я предполагаю, что все, что я здесь делаю, опасно, поскольку сам NSBlock является приватным и может измениться. - person donalbain; 06.09.2011
comment
Если вы хотите вызвать блок, вы можете просто сделать это block(); вместо [блокировать вызов]; stackoverflow.com/a/9484268 - person unom; 12.08.2017