代码之家  ›  专栏  ›  技术社区  ›  Kevlar

为什么在obj-c中调用未实现的可选协议方法时会导致运行时错误?

  •  10
  • Kevlar  · 技术社区  · 15 年前

    我有两个类可以充当第三个类的委托,它们都实现了完全由可选方法构成的正式协议。其中一个类实现了所有东西,而另一个类只实现了我关心的几个方法。但是,在运行时,当我让第二个类充当第三个类的委托,第三个类最终在该委托上调用了一个未实现的可选方法时,我得到了一个运行时错误,基本上是说“Target不响应此消息选择器”。我认为objective-c正确地处理了这种情况,如果该方法没有在类上实际定义,它将什么也不做。也许我遗漏了什么?

    4 回复  |  直到 15 年前
        1
  •  34
  •   Ben Gotow Dmitry Nogin    15 年前

    当您调用委托的可选方法时,您需要确保它在调用之前响应选择器:

    if ([delegate respondsToSelector:@selector(optionalMethod)])
        [delegate optionalMethod];
    
        2
  •  10
  •   Peter N Lewis    15 年前

    可选协议方法只是意味着实现该协议的对象不必实现所讨论的方法——然后被调用方必须在调用之前检查对象是否实现了该方法(否则,您会崩溃,正如您所注意到的)。这些NSObject HOM类别可能会有所帮助:

    @implementation NSObject (Extensions)
    
    - (id)performSelectorIfResponds:(SEL)aSelector
    {
        if ( [self respondsToSelector:aSelector] ) {
            return [self performSelector:aSelector];
        }
        return NULL;
    }
    
    - (id)performSelectorIfResponds:(SEL)aSelector withObject:(id)anObject
    {
        if ( [self respondsToSelector:aSelector] ) {
            return [self performSelector:aSelector withObject:anObject];
        }
        return NULL;
    }
    
    @end
    

    然后你可以简单地做:

    [delegate performSelectorIfResponds:@selector(optionalMethod)];
    
        3
  •  4
  •   Eli Burke    12 年前

    一旦你对正在发生的事情了如指掌,这个阻塞解决方案就很有效了。我添加了一个BOOL结果,因为我希望能够有条件地运行几个可选方法中的一个。如果您正在尝试实施此解决方案,请提供以下提示:

    首先,如果您还没有遇到扩展/类别,只需将其添加到类的顶部,即现有类定义之外。它将是一个公共或私有的扩展,取决于你把它放在哪里。

    @implementation NSObject (Extensions)
    // add block-based execution of optional protocol messages
    -(BOOL) performBlock:(void (^)(void))block ifRespondsTo:(SEL) aSelector
    {
        if ([self respondsToSelector:aSelector]) {
            block();
            return YES;
        }
        return NO;
    }
    @end
    

    其次,下面是如何从代码中调用它:

    BOOL b = [(NSObject*)self.delegate performBlock:^{
        // code to run if the protocol method is implemented
    } 
    ifRespondsTo:@selector(Param1:Param2:ParamN:)];
    

    代替 Param1:Param2:ParamN: 协议方法的每个参数的名称。每一个都应该以冒号结尾。 因此,如果您的协议方法如下所示:

    -(void)dosomething:(id)blah withObj:(id)blah2 andWithObj(id)blah3;

    最后一行是这样的:

    ifRespondsTo:@selector(dosomething:withObj:andWithObj:)];

        4
  •  2
  •   cfischer    13 年前

    -(void) performBlock:(void (^)(void))block ifRespondsTo:(SEL) aSelector{
        if ([self respondsToSelector:aSelector]) {
            block();
        }
    }
    

    通过将此添加到NSObject,您可以有条件地执行任何@optional方法,无论它可能有多少个参数。

    看见 How to safely send @optional protocol messages that might not be implemented