Skip to content

Instantly share code, notes, and snippets.

@mikeash
Created October 21, 2011 02:38
Show Gist options
  • Save mikeash/1302977 to your computer and use it in GitHub Desktop.
Save mikeash/1302977 to your computer and use it in GitHub Desktop.
NSInvocation works for blocks too!
#import <dlfcn.h>
#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 void *BlockImpl(id block)
{
return ((struct Block *)block)->invoke;
}
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];
}
@interface NSInvocation (PrivateHack)
- (void)invokeUsingIMP: (IMP)imp;
@end
typedef void (^BlockInterposer)(NSInvocation *inv, void (^call)(void));
@interface FakeBlock : NSObject
{
int _flags;
int _reserved;
IMP _invoke;
struct BlockDescriptor *_descriptor;
id _forwardingBlock;
BlockInterposer _interposer;
}
- (id)initWithBlock: (id)block interposer: (BlockInterposer)interposer;
@end
@implementation FakeBlock
- (id)initWithBlock: (id)block interposer: (BlockInterposer)interposer
{
if((self = [super init]))
{
_forwardingBlock = [block copy];
_interposer = [interposer copy];
_invoke = [self methodForSelector: @selector(thisDoesNotExistOrAtLeastItReallyShouldnt)];
}
return self;
}
- (void)dealloc
{
[_forwardingBlock release];
[_interposer release];
[super dealloc];
}
- (NSMethodSignature *)methodSignatureForSelector: (SEL)sel
{
const char *types = BlockSig(_forwardingBlock);
NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes: types];
while([sig numberOfArguments] < 2)
{
types = [[NSString stringWithFormat: @"%s%s", types, @encode(void *)] UTF8String];
sig = [NSMethodSignature signatureWithObjCTypes: types];
}
return sig;
}
- (void)forwardInvocation: (NSInvocation *)inv
{
[inv setTarget: _forwardingBlock];
_interposer(inv, ^{
[inv invokeUsingIMP: BlockImpl(_forwardingBlock)];
});
}
@end
id ForwardingBlock(BlockInterposer interposer, id block)
{
return [[[FakeBlock alloc] initWithBlock: block interposer: interposer] autorelease];
}
int main(int argc, char **argv)
{
@autoreleasepool
{
void (^block)(int) = ForwardingBlock(^(NSInvocation *inv, void (^call)(void)) {
[inv setArgument: &(int){ 4242 } atIndex: 1];
call();
}, ^(int testarg){
NSLog(@"%d %d", argc, testarg);
});
block(42);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment