This is primarily a curiosity, I'm not really sure what's the practical use of this but here goes.
Since blocks are also Objective-C objects, is it possible to check their type? That is, does it respond to the isKindOfClass:
message and how to use that message with respect to blocks?
My naive thought that it's probably like this:
-(void) aMethod {
typedef int (^BlockA)(int x, int y);
id blockVar = ...; // get a block from somewhere
if([blockVar isKindOfClass:BlockA]) {
BlockA blockVarA = blockVar;
int result = blockVarA(1,2);
}
}
The code above probably won't work. But if it is possible to check a block's type, what is the correct way to do it?
Can do, kinda sorta.
But first, let's disambiguate. -[NSObject isKindOfClass:]
can tell you it's a block, and that's about it. E.g. I believe this line of code -- ostensibly & unfortunately A BAD IDEA -- will return YES for blocks on present Lion & iOS 5.x:
[myBlock isKindOfClass:NSClassFromString(@"NSBlock")]
That won't help you distinguish the block's function signature.
But it can be done, by snagging the signature from the block's documented internal struct. Code follows for an example OS X command-line app, much of which ripped from Mike Ash's MABlockClosure (great detailed explanation). (UPDATE: Github project CTObjectiveCRuntimeAdditions also apparently provides library code for just this purpose.)
#import <Foundation/Foundation.h>
struct BlockDescriptor {
unsigned long reserved;
unsigned long size;
void *rest[1];
};
struct Block {
void *isa;
int flags;
int reserved;
void *invoke;
struct BlockDescriptor *descriptor;
};
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];
}
int main(int argc, const char * argv[])
{
@autoreleasepool {
int (^block)(NSNumber *) = ^(NSNumber *num) {
NSLog(@"%@ %@", NSStringFromClass([num class]), num);
return [num intValue];
};
NSLog(@"signature %s", BlockSig(block));
NSLog(@"retval %d", (int)block([NSNumber numberWithInt:42]));
}
return 0;
}
Run this and you should get something like:
[58003:403] signature i16@?0@8
[58003:403] __NSCFNumber 42
[58003:403] retval 42
The numbers in the signature (I'm told they are offsets) can be stripped for simpler i@?@
.
The signature is in the @encode format, which isn't perfect (e.g. most objects map to same @
), but should afford you some ability to distinguish blocks with different signatures at runtime.
While it's not documented in the Apple link, my testing points to @?
being the code for a block type, which makes sense of the signature above. I found a clang-developers discussion on this issue which seems to back this up.