这主要是好奇心,我不确定这个实际用途是什么,但是这里有。
由于块也是Objective-C对象,是否可以检查它们的类型?也就是说,它是否响应isKindOfClass:
消息以及如何对块使用该消息?
我的天真认为它可能是这样的:
-(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);
}
}
上面的代码可能不起作用。但如果 可以检查块的类型,那么正确的方法是什么?
答案 0 :(得分:42)
可以做,有点儿。
但首先,让我们消除歧义。 -[NSObject isKindOfClass:]
可以告诉你这是一个障碍,就是这样。例如。我相信这个代码行 - 表面上和&不幸的是, A BAD IDEA - 对于现在的Lion& iOS 5.x:
[myBlock isKindOfClass:NSClassFromString(@"NSBlock")]
这无助于区分块的功能签名。
但是可以通过从块的文档内部结构中获取签名来完成。代码遵循一个示例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格式,这并不完美(例如,大多数对象映射到相同的@
),但应该为您提供某些区分块的能力在运行时使用不同的签名。
虽然没有在Apple链接中记录,但我的测试指向@?
是块类型的代码,这可以理解上面的签名。我在这个问题上找到了一个铿锵的开发者discussion,似乎支持这个。
答案 1 :(得分:11)
BlockA
中的“(^BlockA)
”是变量名称(在本例中为typedef),而不是其类。
块是对象,但不是NSObject
的常规子类。它们只实现方法的一个子集。 -isKindOfClass:
可能会崩溃
块的类型为NSMallocBlock
或NSConcreteGlobalBlock
,...取决于它们的创建位置(堆,堆栈,......)。
答案 2 :(得分:8)
似乎块是__NSGlobalBlock__
,__NSStackBlock__
或__NSMallocBlock__
等类,其继承链最终转到NSBlock
然后{{1} }。因此,可以通过执行NSObject
来测试是否存在某个块。但是,似乎没有任何方法可以在运行时查询块的签名(返回类型和参数类型),因此您将无法区分不同签名的块。
答案 3 :(得分:3)
除了苹果公司没有任何我可以在这个问题上发表的言论之外,用class_copyMethodList
和method_getName
来阻止一个块显示没有明显暴露的方法。所以我要说的是检查它们的类型是不可能的。
答案 4 :(得分:1)
一个老问题,但无论如何:
如果你想要一个简单的方法:(用-fno-objc-arc编译)
Class __NSGlobalBlock__CLASS () {
static Class result = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dispatch_block_t thisIsAGlobalBlock = ^{// a block with no variables will be a __NSGlobalBlock__
};
result = [[thisIsAGlobalBlock class] retain];
});
return result;
};
Class __NSStackBlock__CLASS () {
static Class result = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__block dispatch_block_t thisIsAStackBlock = ^{
return ;// we really DON'T want infinate recursion
thisIsAStackBlock();// including a reference to __block var makes this a __NSStackBlock__
};
result = [[thisIsAStackBlock class] retain];
});
return result;
};
Class __NSMallocBlock__CLASS () {
static Class result = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__block dispatch_block_t thisIsAMallocBlock = Block_copy(// << turns the __NSStackBlock__ Block into a __NSMallocBlock__
^{
return ;// we really DON'T want infinate recursion
thisIsAMallocBlock();// including a reference to __block var makes this a __NSStackBlock__
});
result = [[thisIsAMallocBlock class] retain];
Block_release(thisIsAMallocBlock);
});
return result;
};
测试代码:
@autoreleasepool {
__block dispatch_block_t iAmAGlobalBlock = ^{
};
__block dispatch_block_t iAmAStackBlock = ^{
return;
iAmAStackBlock();
};
dispatch_block_t iAmHeapBlock = Block_copy(iAmAStackBlock);
dispatch_block_t iAmNotAHeapBlock = Block_copy(iAmAGlobalBlock);
if ([iAmAGlobalBlock isKindOfClass:__NSGlobalBlock__CLASS()]) {
NSLog(@"very great success!");
}
if ([iAmAStackBlock isKindOfClass:__NSStackBlock__CLASS()]) {
NSLog(@"another great success!");
}
if ([iAmHeapBlock isKindOfClass:__NSMallocBlock__CLASS()]) {
NSLog(@"also great success!");
}
if ([iAmNotAHeapBlock isKindOfClass:__NSGlobalBlock__CLASS()]) {
NSLog(@"yet another great success!");
}
NSLog (@"Block classes, as reported by NSStringFromClass():\n__NSGlobalBlock__CLASS() = %@\n__NSStackBlock__CLASS() = %@\n__NSMallocBlock__CLASS() = %@\n[iAmAGlobalBlock class] = %@\n[iAmAStackBlock class] = %@\n[iAmHeapBlock class] = %@\n[iAmNotAHeapBlock class] = %@\n",
NSStringFromClass(__NSGlobalBlock__CLASS()),
NSStringFromClass(__NSStackBlock__CLASS()),
NSStringFromClass(__NSMallocBlock__CLASS()),
NSStringFromClass([iAmAGlobalBlock class]),
NSStringFromClass([iAmAStackBlock class]),
NSStringFromClass([iAmHeapBlock class]),
NSStringFromClass([iAmNotAHeapBlock class])
);
Block_release(iAmHeapBlock);
Block_release(iAmNotAHeapBlock);// not really needed, but since we did "Block_copy" it...
}