Can an Obj-C Block execute itself?
Asked Answered
H

2

8

This is an extension of this queston: Is it possible to create a category of the "Block" object in Objective-C.

Basically while it seems possible to create a category on blocks, either through NSObject or NSBlock, I'm having trouble understanding how the block would be able to evaluate itself. The example given in the answer to the last question:

- (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
}

Implies that it is possible to somehow cast self to a block variable, but how would one execute the block itself? For example, say I did a category on NSBlock and in a method did:

NSBlock* selfAsBlock = (NSBlock*)self;

Is there any message I can send to selfAsBlock to have the block evaluate?

Huba answered 6/9, 2011 at 12:52 Comment(2)
Yes you can. You can find more in this (already on SO) #4825113Westleigh
I don't see how that solves this problem. That answer seems to focus on how to call a block from within its own block definition. I'm talking about evaluating a block from the block object itself. Just to be a bit more specific, what I'm hoping to achieve with this is to be able to add methods to blocks (either on NSObject or NSBlock) to do block based control flow (i.e. [block whileTrueDo: block]). To do this I need the block to reevaluate itself in the method.Huba
K
7

Implies that it is possible to somehow cast self to a block variable

Like this:

- (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));
}

Note that (in most cases) you need to fix the block signature so that the compiler is able to set up the call site according to the target platform ABI. In the example above, the signature is return type int, single parameter of type int.

A full example is:

#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;
}
Kirst answered 6/9, 2011 at 13:41 Comment(2)
Great. Is the runtime explicit dynamic method add code necessary though? Or can this also be done through a category?Huba
@don As explained in the answers to the question you’ve linked, I don’t think it’s possible to use a category since the compiler needs the original interface declaration when parsing a category.Kirst
B
4

NSBlock has an invoke method that can be used to call the block.

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

Note that this is a private, undocumented method.

Brainstorm answered 6/9, 2011 at 13:41 Comment(2)
That's the kind of method I was looking for. Of course I guess everything I'm doing here is dangerous since NSBlock itself is private and could change.Huba
If you want to invoke the block you can simply do this block(); instead of [block invoke]; stackoverflow.com/a/9484268Semiweekly

© 2022 - 2024 — McMap. All rights reserved.