小萝莉说Crash(一):Unrecognized selector sent to instance xxxx

大家好,我是来自Bugly Crash实验室的小萝莉(害羞ing),很高兴能和大家一起讨论关于移动终端App的Crash问题及解决方法。

在上次的“精神哥讲Crash”系列中,精神哥给大家分享了小白埋坑,让人泪奔的惨痛经历。这或许会让广大机器猿大呼多么痛的领悟,而以为高大上的水果猿也庆幸还是水果靠谱。然而,coding路上,哪有不挖坑的小白,哪有不被坑的小猿呢?

从本周开始,萝莉会定期给大家分享苹果猿(iOS移动开发者)挖坑和埋坑的一些经历。

今天,首先要给大家讲的是一个入(xiao)门(bai)必(mai)现(keng)的Crash类型 - NSInvalidArgumentException的一个错误问题unrecognized selector sent to instance xxxx

Crash基本介绍

错误类型

NSInvalidArgumentException

错误原因

unrecognized selector sent to instance xxxx

错误释义

给实体对象发送了不认识的消息,即对象调用方法出错(方法不存在或对象已被release)

错误基本原因

Objective-C的方法调用其实是基于消息传递的机制,并且是动态编译。因此在编译阶段不会进行类和方法的绑定,而是在运行时执行绑定操作。当类的方法没有实现或对象被提前release时,这个问题会在运行时表现出来,从而导致App崩溃。

影响力

出现率及出错量均在前10,基本上算是小白必遇

下面,我们就通常会出现此类异常的几种常见场景做一个简单分析。

出错场景分析

1.一个符号引发的血案

示例:

... 
// 定义后台加载数据任务 
[obj performSelectorInBackground:@selector(loadDataOnBackground:) withObject:0]; 
...

// 实现selector方法
- (void)loadDataOnBackground{    
... 
}

错误分析: 定义的 selector 方法为带参数的形式,注意方法名后有冒号“:”,而代码中实现的为无参的方法。

正确的方法实现应如下样式:

- (void)loadDataOnBackground:(id) sender{
   ...    
}

在代码中我们通常对Objective-C对象设置selector方法实现事件监听、延时操作或异步操作,但定义后忘记实现方法或方法名书写错误都是常有的事,尤其是在代码量变大,代码结构和注释不够完善的情况下。

此类问题在编译阶段会有警告信息,只要稍加留意就可以修正。

开发者建议:

* 确定 selector 定义使用的流程,即定义后马上实现,并检查是否带参数(方法名是否“:”结尾)
* 合理使用 #pragma 标记组织代码结构
* 不要简单忽略编译过程的警告选项,编译阶段的警告在运行时就可能造成应用崩溃

2.“虚伪”的代理者

示例:

// 定义delegate发送请求 
@protocol RequestDelegate <NSObject>
- (BOOL)sendRequest:(id) req; 
@end

// 发送请求管理器 
@implementation RequestManager
-(BOOL)sendPostRequest:(id) req {    
    ...    
    if (delegate) {
        ...
        return [delegate sendRequest:req];    
    }    
    ...    
    return NO; 
}

错误分析: delegate 是在开发复杂App时必定会用到的机制,通常地,delegate 被定义为id类型,其被设置的实例可能没有实现 RequestDelegate 方法,此时调用sendRequest方法就会出现崩溃。

一般规范的做法是在调用前使用respondsToSelector:(SEL) aSelector方法进行判断,如下:

-(BOOL)sendPostRequest:(id) req {
    ...
    if (delegate && [delegate respondsToSelector:@selector(sendRequest:)]) {
        ...
        return [delegate sendRequest:req];
    }
    ...
    return NO; 
}

开发者建议:

* delegate 方法调用前进行 respondsToSelector 判断
* 实现 delegate 时,立即实现对应的 delegate 方法并添加相应注释

3.对象都去哪儿了

示例:

 // 定义属性与同名变量 
 @interface RequestManager : NSObject {
     id delegate; 
 } 
 @property (nonatomic, retain) id delegate;

 - (id)initWithDelegate:(id) _dele; 
 @end

 @implementation RequestManager 
 @synthesize delegate;
 - (id)initWithDelegate:(id) _dele{
     self = [super init];
     if (self) {
         delegate = _dele; // 没有添加引用计数,应该使用self.delegate = _dele;
     }
     return self; } 
 @end

错误分析: 在初始化方法中,没有调用setter方法对属性赋值,因此没有添加引用计数,这样在使用self.delegate时,有可能已经被release了,此时应用就会崩溃。

这种场景是出现此类问题最多的一种情况,尤其是在非ARC模式的项目中,对象的retainrelease手动控制,更易导致此类问题。

开发者建议:

* 属性和成员变量不要重名定义,合理使用 synthesize 生成属性的 setter 和 getter 方法
* 变量的 retain 和 release 要谨慎,建议采用安全 release 方法,即 release 的对象置为 nil

小结

以上就是给大家分享的关于unrecognized selector sent to instance xxxx异常的内容,其列举的场景并不能完全覆盖我们开发过程中碰到此类问题的所有情况。但万变不离其宗,此类问题的核心就是指向对象的地址出现问题,导致方法调用不成功。

因此,规范的使用API和Objective-C的机制是避免此类问题的前提,而对于此类问题,一般也是建议开发人员在调式阶段能够发现并解决,而非简单规避。当然,为了应用在发布后的稳定性,我们也可以通过forwardInvocation机制避免应用出现崩溃。

后续小萝莉也会跟大家分享如何调式定位此类问题及forwardInvocation的使用方法。

感谢大家的阅读和关注,敬请期待下次腾讯Bugly-Crash实验室推出的“小萝莉说Crash”和“精神哥讲Crash”系列文。

原文发布于微信公众号 - 腾讯Bugly(weixinBugly)

原文发表时间:2014-12-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Ldpe2G的个人博客

ScalaMP ---- 模仿 OpenMp 的一个简单并行计算框架

这个项目是一次课程作业,要求是写一个并行计算框架,本人本身对openmp比较熟,

2096
来自专栏叁金大数据

自学Python五 爬虫基础练习之SmartQQ协议

  BAT站在中国互联网的顶端,引导着中国互联网的发展走向。。。既受到了多数程序员的关注,也在被我们所惦记着。。。

2543
来自专栏calvin

centos7 lldb 调试netcore应用的内存泄漏和死循环示例(dump文件调试)

lldb工具的安装,linux下netcore如何生成dump文件,查看下文 centos7使用lldb调试netcore应用转储dump文件

2593
来自专栏ShaoYL

iOS 安全

3536
来自专栏JarvanMo的IT专栏

Flutter实战:手把手教你写Flutter Plugin

如果你对移动端有所关注,那么你一定会听说过Flutter。得益于Google,Flutter一经推出便得受到了广泛关注。很多开发者跃跃欲试,国内部分大厂,诸如美...

1.5K2
来自专栏信安之路

【作者投稿】一道反序列化CTF引起的思考

刚开始看到这道题目,我是懵逼的。因为整篇代码没有数据输入口,然后怀疑有其它机关,抓包、扫目录无果之后,找到了一篇writeup如下:

1130
来自专栏Crossin的编程教室

【我问Crossin】程序猿该如何正确的使用搜索引擎?

1 调用类的方法时报错 代码: class Foo(): def myMethod(): print('Hello!') a = Foo...

3737
来自专栏互扯程序

毕业季,跳槽季,不刷点面试题怎么能行?

现在是资源共享的时代,同样也是知识分享的时代,如果你觉得本文能学到知识,请把知识与别人分享。 前言 马上就是一年一度的毕业季 跳槽季,找工作三大要素,简...

3525
来自专栏C语言及其他语言

【编程经验】C语言中EOF是什么意思

C语言中EOF的意思 今天跟大家说道说道这个C语言中EOF是什么意思。 相信很多朋友在学习C语言过程中,都看到过EOF的字样,但翻过整本C语...

4037
来自专栏北京马哥教育

练了一年再来总结的 Vim 使用技巧

1483

扫码关注云+社区

领取腾讯云代金券