这主要是出于好奇,我不太确定它的实际用途是什么,但这里是。
由于 block 也是 Objective-C 对象,是否可以检查它们的类型?也就是说,它是否响应 isKindOfClass:
消息以及如何针对 block 使用该消息?
我天真的以为大概是这样的:
-(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);
}
}
上面的代码可能行不通。但是,如果可以检查 block 的类型,那么正确的方法是什么?
最佳答案
可以,有点。
但首先,让我们消除歧义。 -[NSObject isKindOfClass:]
可以告诉你它是一个 block ,仅此而已。例如。我相信这行代码——表面上但不幸的是一个坏主意——将为当前 Lion 和 iOS 5.x 上的 block 返回 YES:
[myBlock isKindOfClass:NSClassFromString(@"NSBlock")]
这不会帮助您区分块的函数签名。
但这可以通过从 block 记录的内部结构中获取签名来完成。下面是一个示例 OS X 命令行应用程序的代码,其中大部分摘自 Mike Ash 的 MABlockClosure。 (很棒detailed explanation)。 (更新: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 (^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;
}
运行这个,你应该得到类似的东西:
[58003:403] signature i16@?0@8
[58003:403] __NSCFNumber 42
[58003:403] retval 42
可以去除签名中的数字(我听说它们是偏移量)以简化i@?@
。
签名在@encode中格式,这并不完美(例如,大多数对象映射到相同的 @
),但应该让您一些能够在运行时区分具有不同签名的 block 。
虽然它没有记录在 Apple 链接中,但我的测试指向 @?
是 block 类型的代码,这使得上面的签名有意义。我找到了一个 clang-developers discussion在这个似乎支持这一点的问题上。
关于objective-c - 检查 Objective-C block 类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9048305/