Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find out how many arguments a block needs

Let's say I have an array containing Blocks, and I need to assert that all of them expect a given number of arguments.

Is there a way to find this out programmatically?

like image 329
Lio Avatar asked Feb 19 '23 19:02

Lio


1 Answers

This is indeed possible, for any recent version of Clang.

The Apple ABI for Blocks is private but also published. Since that document tells us the layout the compiler will use for a Block object, we can duplicate that information in a header file and use it to access the components of a Block.

Mike Ash's MABlockForwarding project does just that (see also the article) -- much of the stuff at the top of this file is a copy-paste from the ABI doc. The thing that he created which we are interested in is the BlockSig() function:

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

    assert(block->flags & BLOCK_HAS_SIGNATURE);

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

    return descriptor->rest[index];
}

which will return (for Blocks that have it (which they all do with recent Clang)), a type encoding string describing the Block's return and argument types. From there, you can create an NSMethodSignature object, and ask it for its numberOfArguments:

 NSString * (^block)(int, NSArray *) = ^NSString * (int i, NSArray * a){
        return @"Oh, yeah!";
 };
 const char * types = BlockSig(block);
 NSMethodSignature * sig = [NSMethodSignature signatureWithObjCTypes:types];
 [sig numberOfArguments];

The result there is 3, because it includes a hidden argument for the Block itself (and Blocks don't use the hidden _cmd argument or it would be 4).

like image 133
jscs Avatar answered Mar 03 '23 22:03

jscs