如何在Objective-c中获取运行时块类型元数据?

Påh*_*lin 7 objective-c objective-c-runtime objective-c-blocks

我正在编写一个类,您可以在其中注册一个对象和一个要观察的属性.当属性设置为非特性时nil,将调用已注册的回调选择器(如target-action).选择器可以具有三个不同的签名,并且根据注册的类型调用右边的签名.

这工作正常,但现在我想添加注册块而不是选择器作为"回调函数"的功能.是否可以找到所提供的块的函数签名并根据所提供的块的类型以不同方式处理回调?

例如:

- (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?
    }
}
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?

我知道我可以使用特定的typedef'ed Block参数创建不同的寄存器函数,但如果可能的话,我宁愿使用单个函数.

Chr*_*eux 5

如果您正在使用clang进行编译,则可以获取此信息.

来自Clang块ABI规范:

块的ABI由它们的布局和编译器所需的运行时函数组成.块由以下形式的结构组成:

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
};
Run Code Online (Sandbox Code Playgroud)

因此,对于可能的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), 
};
Run Code Online (Sandbox Code Playgroud)

实际上,clang的当前版本确实编码签名信息.根据这里的评论,格式只是标准的Objective-c方法编码字符串.也就是说,签名格式没有记录(就像我所知道的那样),所以我不确定你可以假设它不会破坏编译器更新.