super respondsToSelector:返回true但实际上调用super(selector)给出“无法识别的选择器发送到实例”

时间:2012-08-31 18:18:57

标签: objective-c delegates uigesturerecognizer super respondstoselector

好的,我有点困惑。

我有一个UIScrollView的子类,这是我尝试像UI元素一样横向滚动“表格视图”。 UIScrollView本身设置它在内部使用的UIGestureRecognizers,它似乎将自己设置为那些UIGestureRecognizers的委托。我在我的水平表元素/单元格上也有自己的UIGestureRecognizer设置,我自己的类设置为我自己的UIGestureRecognizer的委托。由于我的类是UIScrollView的子类,在运行时,UIGcureRecognizer委托调用来到我的类,用于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 gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]

但这可能没问题。但问题在于它表示它会做出响应,然后却没有。

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

感谢您的任何见解。

5 个答案:

答案 0 :(得分:23)

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

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

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

答案 1 :(得分:17)

[super respondsToSelector:@selector(frobnosticate:)]没有按你的想法行事。

它转到超类,在那里获得respondsToSelector:的实现,然后在当前对象上运行它。换句话说,super表示与self相同的对象,它只是在继承树中将方法查找提高了一步。

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

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

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

避免硬编码类名。

答案 2 :(得分:5)

在查看其他答案后,最佳解决方案是使用[[MapControllerSublcass1 superclass] instancesRespondToSelector:_cmd]。如果您使用上面推荐的内容(例如[BaseClass instancesRespondToSelector:_cmd]),则会遇到更改类层次结构的问题,并且意外忘记将BaseClass更改为新的超类或子类。

[[self superclass] instancesRespondToSelector:...]不正确,正如上面评论中所解释的那样,Apple's documentation (See respondsToSelector: in NSObjct)实际上是这样说的。它仅在您具有1级子类时才有效,因此它为您提供 幻觉 它是一个实际的解决方案。我为它而堕落。

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

例如,我有一个BaseMapController来实现MKMapViewDelegate中的某些方法,但它没有实现mapView:regionWillChangeAnimated:MapControllerSublcass1继承自BaseMapControllerMapControllerSubclass2继承自MapControllerSublcass1

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

<强> MapControllerSublcass1.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];
  }
}

答案 3 :(得分:0)

致电时

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

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

致电时

[super gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer];

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

看起来jjburka首先得到它 - 你需要打电话

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

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

答案 4 :(得分:0)

作为具有相同案例的人的摘要,原始问题中存在两个问题:

  1. 检查超类是否响应该选择器,@ jjburka建议最好使用instancesRespondToSelector:
  2. 实际上,在没有编译器抱怨的情况下调用超类上的方法,即使它是在私有头中声明的。通过在类别中重新声明它可以巧妙地实现这一点(参见this question)。
  3. 把它放在一起就可以了:

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