调用JSQMessageViewController子类的selector报错,父类已实现该方法,为何?
这个问题看起来有点绕,但核心其实和Objective-C的方法可见性、继承逻辑以及JSQMessageViewController的内部实现细节有关。我来帮你拆解一下:
为什么MessagesVc不用重写就能正常调用scrollViewDidScroll:?
JSQMessageViewController本身就是UIScrollViewDelegate的实现者,它内部已经处理了滚动事件。当你子类化它得到MessagesVc时,MessagesVc的scrollView的delegate默认就是自己,由于继承关系,滚动事件触发时,消息会沿着继承链向上查找,最终找到JSQMessageViewController的scrollViewDidScroll:实现——这个过程不需要你在子类里重写,系统会自动完成消息转发。
为什么JSQBaseViewController重写并调用super.scrollViewDidScroll:会报错?
大概率是以下几个原因之一:
1. 父类的scrollViewDidScroll:方法未在公开头文件中显式声明
虽然JSQMessageViewController遵守UIScrollViewDelegate协议,但有些第三方库会把协议方法的实现放在.m文件里,不在公开头文件中重复声明这个方法。在Objective-C中,如果子类调用super的某个方法,但该方法在父类公开头文件中找不到声明,编译器会对方法签名做隐式推断,一旦推断错误,就会导致运行时触发unrecognized selector sent to instance错误。
2. 重写的方法签名有误
检查JSQBaseViewController里的scrollViewDidScroll:是不是写错了:
- 有没有漏掉参数
(UIScrollView *)scrollView? - 方法名拼写错误?比如写成
scrollViewDidScrolled:或者少了末尾的冒号? - 返回值是不是
void?
如果方法签名和父类的不匹配,调用super时自然找不到对应的方法。
3. 调用selector时的语法错误
如果是你手动调用这个selector(比如用performSelector:),有没有把selector写成@selector(scrollViewDidScroll)而不是@selector(scrollViewDidScroll:)?少了冒号意味着这是无参方法,和父类的有参方法完全是两个不同的selector,肯定会报错。
怎么解决?
针对不同的原因,对应解决方案:
如果是头文件声明问题:你可以在
JSQBaseViewController.m的顶部类扩展中,显式声明这个方法,告诉编译器父类有这个方法:// 在JSQBaseViewController.m的顶部 @interface JSQMessageViewController () - (void)scrollViewDidScroll:(UIScrollView *)scrollView; @end这样编译器就能正确识别父类的方法签名,不会再出现推断错误。
如果是方法签名错误:仔细核对你的重写方法,确保和协议定义的完全一致:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { [super scrollViewDidScroll:scrollView]; // 你的自定义逻辑 }如果是selector调用错误:检查所有调用这个方法的地方,确保selector是
@selector(scrollViewDidScroll:)(注意末尾的冒号)。
另外,如果你用的是比较旧版本的JSQMessageViewController,也可以看看官方更新日志,有没有关于这个方法的修改——不过大概率还是前面提到的方法可见性或者签名问题。
内容的提问来源于stack exchange,提问作者Chen Li Yong

