【问题标题】:super respondsToSelector: returns true but actually calling super (selector) gives "unrecognized selector sent to instance"super respondsToSelector:返回 true,但实际上调用 super(选择器)会给出“发送到实例的无法识别的选择器”
【发布时间】:2012-08-31 18:18:56
【问题描述】:

好吧,我有点困惑。

我有一个 UIScrollView 的子类,这是我尝试像 UI 元素一样水平滚动的“表格视图”。 UIScrollView 本身设置了它在内部使用的 UIGestureRecognizers,并且它似乎将自己设置为那些 UIGestureRecognizers 的委托。我的水平表格元素/单元格上也有我自己的 UIGestureRecognizer 设置,我自己的类设置为我自己的 UIGestureRecognizer 的委托。由于我的类是 UIScrollView 的子类,因此在运行时,UIGestureRecognizer 委托调用会针对 UIScrollView 内置 UIGestureRecognizers 和我自己的 UIGestureRecognizers 进入我的类。有点 PITA,但我们可以通过传递我们不关心的那些来解决这个问题。

-(BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer 
{ 
   if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]])
      return NO; 
      else
      {  
        if ([super respondsToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)])
           return [super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
           else 
           return NO;
        }
}

问题是检查 [super respondsToSelector:@selector()] 返回 YES,但是当我实际调用它时 return [super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer]; 我得到以下异常

2012-08-31 12:02:06.156 MyApp[35875:707] -[MyAppHorizo​​ntalImageScroller gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]:无法识别的选择器发送到实例 0x21dd50

我认为它应该显示

-[UIScrollView 手势识别器:shouldRecognizeSimultaneouslyWithGestureRecognizer:]

但这可能没问题。但问题是它说它会响应然后没有响应。

另外两个 UIGestureRecognizer 委托例程使用此代码(显然不同的选择器)。

感谢您的任何见解。

【问题讨论】:

    标签: objective-c delegates uigesturerecognizer super respondstoselector


    【解决方案1】:

    除非您在类中覆盖对选择器的响应,否则您将在调用 super 时使用默认实现,该实现将检查当前实例。如果您想查看某类对象的实例是否响应选择器,请使用+(BOOL)instancesRespondToSelector:(SEL)aSelector;

    这将检查对象及其父对象。因此,在您的情况下,您需要以下内容:

    [UIScrollView instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]
    

    【讨论】:

    【解决方案2】:

    [super respondsToSelector:@selector(frobnosticate:)] 并没有按照你的想法去做。

    它转到超类,在那里获取respondsToSelector: 的实现,然后在当前对象上运行它。换句话说,superself 代表的是同一个对象,它只是在继承树中开始了更高一级的方法查找。

    所以你在这个子类上运行respondsToSelector:,它回答“是”,但后来试图从没有它的超类中获取gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:

    要检查直接超类的实例,您可以使用 instancesRespondToSelector:,正如 jjburka 推荐的那样,但我建议将 [self superclass] 作为主题,如下所示:

    [[self superclass] instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)];
    

    避免硬编码类名。

    【讨论】:

    • Apple 建议不要使用 [[self superclass] instancesRespondToSelector 此处提到的developer.apple.com/library/ios/#documentation/Cocoa/Reference/…:
    • 它在 -respondsToSelector 下,上面写着“你不能简单地使用[[self superclass] instancesRespondToSelector:@selector(aMethod)],因为如果它被子类调用,这可能会导致方法失败。”
    • 我一定是遗漏了一些东西,因为如果子类的超类有一个方法的实现,我看不出它是如何实现的。
    • 如果你再次子类化它。子类调用它的超类,它仍然是原始的子类。如果我有 MyScrollView,它是 UIScrollView 的子类,然后我有 MyOtherScrollView,它是 MyScrollView 的子类,当你在 MyOtherScrollView 中时,[[self superclass] instancesRespondToSelector:@selector(aSelector)][UIScrollView instancesRespondToSelector:@selector(aSelector)] 不一样
    • @JoshCaswell:他的意思是,如果self的类是MyOtherScrollView,并且你在MyScrollView的实例方法中,并且你运行[[self superclass] instancesRespondToSelector:@selector(aSelector)],它将测试MyScrollView 是否响应 aSelector,而不是 UIScrollView 是否响应 aSelector。因此,在 MyScrollView 的相同方法中,即使上面的表达式返回 true,[super aSelector] 仍然可能失败,因为 UIScrollView 可能不会响应该选择器。
    【解决方案3】:

    查看其他答案后,最好的解决方案是使用[[MapControllerSublcass1 superclass] instancesRespondToSelector:_cmd]。如果您使用上面推荐的方法,例如[BaseClass instancesRespondToSelector:_cmd],您会遇到更改类层次结构的问题,并且不小心忘记将BaseClass 更改为新的超类或您的子类。

    [[self superclass] instancesRespondToSelector:...] 不正确,正如上面在 cmets 中所解释的那样,它实际上在 Apple's documentation (See respondsToSelector: in NSObjct) 上是这样说的。它仅在您有 1 级子类化时才有效,因此它让您错觉认为它是一个实际的解决方案。我爱上了它。

    [[super class] instancesRespondToSelector:...] 不起作用,这是这个 SO 问题的重点。

    例如,我有一个BaseMapController,它实现了MKMapViewDelegate 中的一些方法,但它没有实现mapView:regionWillChangeAnimated:MapControllerSublcass1 继承自 BaseMapController。而MapControllerSubclass2 继承自MapControllerSublcass1

    在我的代码中,我有类似的东西,它工作正常。

    MapControllerSublcas1.m

    - (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
      if ([[MapControllerSublcass1 superclass] instancesRespondToSelector:_cmd]) {
        [super mapView:mapView regionWillChangeAnimated:animated];
      }
    }
    

    MapControllerSubclass2.m

    - (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
      if ([[MapControllerSubclass2 superclass] instancesRespondToSelector:_cmd]) {
        [super mapView:mapView regionWillChangeAnimated:animated];
      }
    }
    

    【讨论】:

    • 您可以将[MapControllerSubclass2 superclass] 替换为[self superclass]
    • 不,你不应该。请参阅上面的第二段。
    • 使用super 本身不一定是“错觉”。 Apple 在NSObject Protocol documentation 中注意到了这一点。由于协议在self之上没有抽象,super本质上是self
    【解决方案4】:

    当你打电话时

    [super respondsToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]
    

    这将转到超类并运行respondsToSelector 的实现。这会查看实例(在本例中是您的自定义滚动视图)并确定它是否响应该选择器。

    当你打电话时

    [super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
    

    您尝试使用该方法的超类实现发送消息,在这种情况下该方法不存在,从而导致崩溃。

    看起来 jjburka 先到了 - 你需要打电话

    [UIScrollView instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]
    

    此外,如果您从无法识别的选择器中使用-[super performSelector:],它也不会崩溃 - 执行选择器获取选择器的实例实现。它会因无限递归而崩溃。

    【讨论】:

      【解决方案5】:

      作为同案者的总结,原题有两个问题:

      1. 检查超类是否响应该选择器,正如@jjburka 建议的那样,最好使用instancesRespondToSelector: 来完成。
      2. 实际上在超类上调用该方法而编译器不会抱怨,即使它是在私有头文件中声明的。这可以通过在一个类别中重新声明来巧妙地实现(参见this question)。

      把它放在一起得到:

      // … In subclass implementation file
      @interface UIScrollView () <UIGestureRecognizerDelegate> @end
      
      // … In gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
      if ([UIScrollView instancesRespondToSelector:@selector(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)]) {
          return [super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-06-16
        • 2019-08-28
        • 2012-07-24
        相关资源
        最近更新 更多