I'm writing a class where you register an object and a property to observe. When the property gets set to something non-nil
, a registered callback selector is called (like target-action). The selector may have three different signatures, and the right one is called depending on which type was registered.
This works fine, but now I want to add the ability to register a Block instead of a selector as the "callback function". Is it possible to find out the function signature of the supplied Block and handle the callback differently depending on the type of Block supplied?
For example:
- (void)registerCallbackBlock:(id)block
{
if ([self isBlock:block] {
if ([self isMethodSignatureOne:block]) { /* */ }
else if ([self isMethodSignatureTwo:block]) { /* */ }
else { assert(false); } // bad Block signature
block_ = block; // assuming ARC code
}
else { assert(false); } // not a block
}
- (void)callBlock
{
if ([self isMethodSignatureOne:block_] {
block_(arg1_, arg2_); // needs casting?
}
else if ([self isMethodSignatureTwo:block_) {
block_(arg1_, arg2_, arg3_); // needs casting?
}
}
Any ideas?
I know I can make different register functions with specific typedef
'ed Block arguments but I would rather have a single function, if possible.
If you're compiling with clang, you can get this information.
From the Clang block ABI spec:
The ABI of blocks consist of their layout and the runtime functions required by the compiler. A Block consists of a structure of the following form:
struct Block_literal_1 {
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor_1 {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct Block_literal_1)
// optional helper functions
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
void (*dispose_helper)(void *src); // IFF (1<<25)
// required ABI.2010.3.16
const char *signature; // IFF (1<<30)
} *descriptor;
// imported variables
};
The following flags bits are in use thusly for a possible ABI.2010.3.16:
enum {
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code
BLOCK_IS_GLOBAL = (1 << 28),
BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
BLOCK_HAS_SIGNATURE = (1 << 30),
};
In practice, the current version of clang does encode signature information. According to a comment here, the format is just a standard objective-c method encoding string. That said, the signature format isn't documented (as fas as I can tell), so I'm not sure how much you can assume it won't break with compiler updates.
I don't believe this is actually possible with the current ABI. However, you could do something like this:
- (void)registerSimpleCallback: (BlockWith2Args)block {
BlockWith3Args wrapper = ^ (id arg1, id arg2, id arg3) {
block(a, b);
};
[self registerComplexCallback: wrapper];
}
- (void)registerComplexCallback: (BlockWith3Args)block {
[block_ release];
block_ = [block copy];
}
- (void)callBlock {
if (block_)
block_(arg1, arg2, arg3);
}
Yes we can, as I answered in another question.
The block struct is a published clang standard, and AFAIK, can be used all you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With