检查Objective-C块类型? [英] Checking Objective-C block type?

查看:135
本文介绍了检查Objective-C块类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这主要是一个好奇心,我不确定这是什么的实际用途,但这里。



由于块也是Objective-C对象,它可以检查他们的类型?也就是说,它是否响应 isKindOfClass:消息以及如何使用该消息相对于块?



我的天真想到它可能是这样的:

   - (void)aMethod {
typedef int(^ BlockA) int x,int y);
id blockVar = ...; //从某处获取块
if([blockVar isKindOfClass:BlockA]){
BlockA blockVarA = blockVar;
int result = blockVarA(1,2);
}
}

上面的代码可能不工作。

解决方案



但是首先,让我们来消除歧义。 - [NSObject isKindOfClass:] 可以告诉你它是一个块,这就是它。例如。我相信这行代码 - 表面上&不幸的是 A BAD IDEA 会对现在的Lion& iOS 5.x:

  [myBlock isKindOfClass:NSClassFromString(@NSBlock)] 

这不会帮助你区分块的函数签名。



但它可以通过阻塞来自块的文档内部结构的签名来完成。代码如下是一个示例OS X命令行应用程序,其中许多摘录自Mike Ash的 MABlockClosure (great 详细说明)。 (更新:Github项目 CTObjectiveCRuntimeAdditions 也显然提供了仅用于此目的的库代码。)

  #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 )(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;
}

运行此操作,您应该得到类似的结果:



[58003:403] signature i16 @?0 @ 8
[58003:403] __NSCFNumber 42
[58003:403] retval 42

签名中的数字(我被告知他们是偏移量) code> i @?@ 。



签名位于 @ encode 格式,这是不完美的(例如大多数对象映射到相同 @ ),但应该能够让您在运行时区分具有不同签名的块。



Apple链接,我的测试指向 @?是块类型的代码,这对上面的签名很有意义。我发现一个clang开发者关于这个问题的讨论,似乎支持这个问题。


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.

这篇关于检查Objective-C块类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆