【问题标题】:Objective-C type-check a block?Objective-C 类型检查一个块?
【发布时间】:2012-11-22 11:13:59
【问题描述】:

据我所知,这与 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]);
   }
}

【问题讨论】:

  • 我认为你不能。在你的情况下,你可以留下额外的参数 NSObject ,如果你不使用它,就放 nil 。
  • 在本例中,最好对字典中的所有块使用一致的签名。然后,每个块中的代码可以独立决定使用或忽略哪些参数。您还必须将 -objectForKey: 的返回值转换为您的块签名,然后才能调用该块。您还必须在将每个块添加到字典之前将其复制到堆中。
  • Darren 能否详细说明您的最后两个陈述,谢谢!
  • 首先,您的字典语法无效。您的意思可能类似于@{ @"Foo": ^(...) {...}, @"Bar": ^(...) {...} }。其次,您需要先复制一个块,然后再将其放入字典之类的通用集合中,否则会发生不好的事情。
  • 创建一个 NSOperation 的子类。

标签: objective-c ios objective-c-blocks


【解决方案1】:

坦率地说,如果回调有不同的类型,它们应该在不同的键下。为什么不使用键@"callbackWithOneParam"@"callbackWithTwoParams"?对我来说,这比拥有一个通用的“回调”键和一个单独的“类型”键来告诉你如何解释回调要好。

但这真正需要的是使用自定义类的对象而不是字典。您已经越过了通用对象不再方便并开始引发的问题比它们解决的问题更多的界限。

【讨论】:

  • +1 用于推荐自定义类而不是字典。
【解决方案2】:

就我个人而言,我使用巧妙的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}

华丽...

【讨论】:

    【解决方案3】:

    调用块时,您必须知道其参数的类型。在您的情况下,如果“名称”不足以确定参数应该是什么,我会添加另一个会告诉我的关键“类型”。这是一个例子:

    // 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"];
    
            }
        }
    }
    

    请注意,您必须将块转换为正确的类型,否则您可能会导致程序崩溃。这是因为该块依赖于编译器以正确的顺序将参数放入堆栈并允许任何返回类型。

    【讨论】:

      【解决方案4】:

      只检查名称是“Foo”还是“Bar”?可能与检查函数参数一样多,我觉得如果没有某种形式的课程并继续下去,这是不可能的

      if ([myObject class] == [MyClass class])
      

      【讨论】:

      • 你真的应该为此使用isKindOfClass:isMemberClass:
      猜你喜欢
      • 2012-02-21
      • 2016-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多