检查Objective-C块类型? [英] Checking Objective-C block type?
问题描述
这主要是一个好奇心,我不确定这是什么的实际用途,但这里。
由于块也是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屋!