我所知,这与SO上其他"我可以检查块的类型"帖子不同。
我想知道,给定一个未知签名的块对象,我是否可以在调用之前了解它接受哪些参数?
我遇到一种情况,即我有许多与字典中的对象关联的回调。 我希望其中一些回调期望一组不同的参数。 这里的例子非常简化,但我认为它明白了重点。
如何确定块是否属于我以前键入的定义类型?
//MyClass.m
// I start by declare two block types
typedef void (^callbackWithOneParam)(NSString*);
typedef void (^callbackWithTwoParams)(NSString*, NSObject*);
........
// I create a dictionary mapping objects to callback blocks
self.dict = @{
@"name": "Foo",
@"callback": ^(NSString *aString) {
// do stuff with string
}
}, {
@"name": "Bar",
@"callback": ^(NSString *aString, NSObject *anObject) {
// do stuff with string AND object
}
}
.....
// Later, this method is called.
// It looks up the "name" parameter in our dictionary,
// and invokes the associated callback accordingly.
-(void) invokeCallbackForName:(NSString*)name {
// What is the type of the result of this expression?
[self.dict objectForKey: name]
// I want to say: (pseudocode)
thecallback = [self.dict objectForKey: name];
if (thecallback is of type "callbackWithOneParam") {
thecallback(@"some param")
}
else if (thecallback is of type "callbackWithTwoParams") {
thecallback(@"some param", [[NSObject alloc] init]);
}
}
坦率地说,如果回调具有不同的类型,则它们应该位于不同的键下。为什么不使用@"callbackWithOneParam"
和@"callbackWithTwoParams"
键?对我来说,这比拥有一个通用的"回调"键和一个单独的"类型"键来告诉你如何解释回调要好。
但这真正需要的是使用自定义类的对象而不是字典。您已经越过了通用对象不再方便并开始导致比它们解决的问题更多的边界。
就个人而言,我使用巧妙的CTBlockDescription...
CTBlockDescription 允许您在运行时检查包括参数和编译时功能的块。
BOOL(^bk)(BOOL,id) = ^BOOL(BOOL ani, id obj) { return YES; };
[CTBlockDescription.alloc initWithBlock:bk].blockSignature.description;
<NSMethodSignature: 0x253f080>
number of arguments = 3
frame size = 12
is special struct return? NO
return value: -------- -------- -------- --------
type encoding (c) 'c'
flags {isSigned}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 4, size adjust = -3}
memory {offset = 0, size = 1}
argument 0: -------- -------- -------- --------
type encoding (@) '@?'
flags {isObject, isBlock}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 4, size adjust = 0}
memory {offset = 0, size = 4}
argument 1: -------- -------- -------- --------
type encoding (c) 'c'
flags {isSigned}
modifiers {}
frame {offset = 4, offset adjust = 0, size = 4, size adjust = -3}
memory {offset = 0, size = 1}
argument 2: -------- -------- -------- --------
type encoding (@) '@'
flags {isObject}
modifiers {}
frame {offset = 8, offset adjust = 0, size = 4, size adjust = 0}
memory {offset = 0, size = 4}
华丽。。。
调用块时,您必须知道其参数的类型。在您的情况下,如果"名称"不足以确定参数应该是什么,我会添加另一个键"类型"来告诉我。下面是一个示例:
// Callback dictionary
_callbacks = @{
@{@"name":@"foo", @"type":@(1), @"callback":^(int i) { NSLog(@"%d", i); }},
@{@"name":@"bar", @"type":@(2), @"callback":^(int i, int j) { NSLog(@"%d", i+j); }},
@{@"name":@"zap", @"type":@(3), @"callback":^(int i, int j, int k) { NSLog(@"%d", i+j+k); }},
@{@"name":@"cab", @"type":@(4), @"callback":^(NSString *s) { NSLog(@"%lu",s.length); }},
@{@"name":@"fog", @"type":@(5), @"callback":^(void) { NSLog(@"I can't see"); }}
}
-(void) invokeCallbackForName:(NSString*)name withArguments:(NSArray*)args {
NSDictionary *info = _callbacks[name];
if (info != nil) {
id block = info[@"callback"];
int type = [info[@"type"] intValue];
switch (type) {
case 1: {
int arg1 = [args[0] intValue];
((void(^)(int)) block)(arg1);
break;
}
case 2: {
int arg1 = [args[0] intValue];
int arg2 = [args[1] intValue];
((void(^)(int,int)) block)(arg1,arg2);
break;
}
case 3: {
int arg1 = [args[0] intValue];
int arg2 = [args[1] intValue];
int arg3 = [args[2] intValue];
((void(^)(int,int,int)) block)(arg1,arg2,arg3);
break;
}
case 5: {
NSString *arg1 = [args[0] intValue];
((void(^)(NSString*)) block)(arg1);
break;
}
default:
[NSExceptien raise:NSInvalidArgumentException format:@"Unsupported callback type"];
}
}
}
请注意,必须将块转换为正确的类型,否则程序可能会崩溃。这是因为块依赖于编译器以正确的顺序将参数放在堆栈上并允许任何返回类型。
只检查名称是"Foo"还是"Bar"? 可能与检查函数参数一样多,我有一种感觉,如果没有它在某种形式的类中并去
if ([myObject class] == [MyClass class])