17

I would like to know two things:

1- Is it possible by using objective-c introspection to know the return type of a block.

For example: int (^CountBlock)(NSArray *array) I would like to know the type it will be returning is int.

The second question is:

2- Can I hold a reference to a generic block? What I mean with this, is basically can I do something like id myBlock and with this answer the first question.

What I tried

This kind of stuff is not possible:

id aBlock = ^{

    NSString * aString = @"OMG";

    return aString;
};

aBlock();

As the compiler sees that the id aBlock is not a function or a function pointer.

Rui Peres
  • 25,741
  • 9
  • 87
  • 137
  • 2
    this might help for #1 http://realmacsoftware.com/blog/a-python-script-to-disassemble-a-block-in-lldb – wattson12 Dec 18 '13 at 14:15
  • @Cy-4AH the point is to know the return type of a generic block. (question 2) – Rui Peres Dec 18 '13 at 14:25
  • 1
    Do you have a specific use case that you're trying to solve or are you only interested in what's technically possible? If it's for a specific use case then I'd suggest you re-think your solutions as introspect blocks and casting them is not a good idea. However, if you're only interested in find out what's technically possible then I encourage you to do as many crazy things you can think of! – Benedict Cohen Dec 18 '13 at 15:00
  • @BenedictCohen it's actually for a possible implementation I am thinking of. But it seems a bit too weird to use it. I aim for: "Solve complex problems with simple solutions". – Rui Peres Dec 18 '13 at 15:03
  • @JackyBoy: Have you though about, suppose you can store it generically, and you can get the signature, what do you think that allows you to do? I don't see how it allows you to use the block easily. – newacct Dec 18 '13 at 20:16
  • @newacct the point was to be able (initially) store the block in an `NSValue`. But after some considerations, I would go by another route. – Rui Peres Dec 18 '13 at 21:51
  • 1
    I don't think this qualifies as an answer, but would probably be interesting to you: https://github.com/fjolnir/Tranquil/blob/master/Source/Tranquil/Runtime/TQBlockClosure.m It's a class that wraps arbitrary blocks and generates one that takes all object parameters, which get unboxed to whatever parameters the original block wants. (The encoding it takes is of the format <@ returnType argTypes>) – Fjölnir Dec 19 '13 at 09:24
  • 1
    https://github.com/fjolnir/Tranquil/blob/master/Source/Tranquil/Runtime/TQBoxedObject.m#L461 – Fjölnir Dec 19 '13 at 09:31

2 Answers2

11

1) This answer talks about how to grab the signature of a block. Here's the relevant code:

static const char *BlockSig(id blockObj)
{
    struct Block *block = (void *)blockObj;
    struct BlockDescriptor *descriptor = block->descriptor;

    int copyDisposeFlag = 1 << 25;
    int signatureFlag = 1 << 30;

    assert(block->flags & signatureFlag);

    int index = 0;
    if(block->flags & copyDisposeFlag)
        index += 2;

    return descriptor->rest[index];
}

It's really not ideal, since you just get the @encode string of the signature, which looks like this: @"i16@?0@8". I'd love to see if someone has a better approach.

2) You can definitely cast blocks to and from type id:

typedef void(^MyBlockType)(void);
id aBlock = ^ { };
((MyBlockType)aBlock)();

Of course, if you cast to the wrong block type, you'll end up with undefined behavior.

Community
  • 1
  • 1
godel9
  • 7,340
  • 1
  • 33
  • 53
  • I don't know the original signature of the block, so I cannot typecast it. – Rui Peres Dec 18 '13 at 14:41
  • Can you create a class that stores both a block and some representation of the signature so that you can invoke the block correctly? – godel9 Dec 18 '13 at 14:45
  • That would be something I didn't think of, but yes it would be possible to do that. – Rui Peres Dec 18 '13 at 14:47
  • @godel9: "Can you create a class that stores both a block and some representation of the signature so that you can invoke the block correctly?" Invoking a function with a signature that is known only at runtime, cannot be portably done in C, because it requires knowing the calling convention of the particular architecture, and using assembly to put the arguments in the right places in the registers and stack. – newacct Dec 18 '13 at 20:12
  • @newacct You're absolutely right. I was thinking there was just a handful of block types that were known at compile-time, but there's nothing in the question to back up that assumption. – godel9 Dec 19 '13 at 00:40
1

I think the problem for number 2 is that blocks are named as part of the type definition, e.g.

void (^blockName)(void) = ^{};

A workaround is to define a generic block type:

typedef void(^BlockType)(void);
BlockType myBlock = ^{};
myBlock();

edit: pointed out by @neilco, a much simpler way using dispatch block types (which return nothing and accept no arguments):

dispatch_block_t myBlock = ^{};
myBlock();
neilco
  • 7,964
  • 2
  • 36
  • 41
wattson12
  • 11,176
  • 2
  • 32
  • 34
  • 1
    There's already a built-in block type with that signature. It's called `dispatch_block_t`. – neilco Dec 18 '13 at 14:43
  • So you suggest casting the block type to a different (incorrect for that block) block type? – newacct Dec 18 '13 at 20:14
  • @newacct no this was just an example of a way to have a "type" for a block, i didn't try to match the typdef to either of the given examples – wattson12 Dec 18 '13 at 22:27
  • right but this seems strictly worse than using `id` or `NSObject *`, both of which would be at least correct – newacct Dec 18 '13 at 22:30
  • I misunderstood a bit and thought OP knew the block type, so for that case this is definitely not worse than using id, but yes casting any block to some random block type is not a good idea – wattson12 Dec 18 '13 at 22:33