【小萝莉说Crash】第一期:Unrecognized selector sent to instance xxxx

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

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

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

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


Crash基本介绍

错误类型 NSInvalidArgumentException
错误原因 unrecognized selector sent to instance xxxx
错误释义 给实体对象发送了不认识的消息,即对象调用方法出错(方法不存在或对象已被release)
错误基本原因 Objective-C的方法调用其实是基于消息传递的机制,并且是动态编译。因此在编译阶段不会进行类和方法的绑定,而是在运行时执行绑定操作。当类的方法没有实现或对象被提前release时,这个问题会在运行时表现出来,从而导致App崩溃。
影响力 出现率及出错量均在前10,基本上算是小白必遇

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


 出错场景分析

1.一个符号引发的血案

示例:

...
// 定义后台加载数据任务
[objperformSelectorInBackground:@selector(loadDataOnBackground:) withObject:0];
...
 
// 实现selector方法
-(void)loadDataOnBackground{   
...
}

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

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

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

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

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

开发者建议:

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


2.“虚伪”的代理者

示例:

// 定义delegate发送请求
@protocolRequestDelegate <NSObject>
-(BOOL)sendRequest:(id) req;
@end
 
// 发送请求管理器
@implementationRequestManager
-(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 && [delegaterespondsToSelector:@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生成属性的settergetter方法
*
变量的 retainrelease要谨慎,建议采用安全release方法,即release的对象置为nil


小结

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

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

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

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


本文系腾讯Bugly特邀文章,转载请注明作者和出处“腾讯Bugly(http://bugly.qq.com)”


### 回答1: 在iOS开发中,当我们运行程序时,有时候会遇到"appDelegate window: unrecognized selector sent to instance"的错误。这个错误发生的原因是我们使用了一个未定义的方法。 通常,这个错误是由于我们在我们的代码中使用了一个不存在的方法。具体来,"appDelegate"是我们的应用程序的代理类,"window"是一个窗口对象。这个错误的意思是我们在"appDelegate"实例上调用了一个名为"window"的方法,但是这个方法并不存在。 为了解决这个问题,我们需要检查我们的代码,找到在"appDelegate"实例上调用"window"方法的地方。一旦我们找到了这个地方,我们可以考虑以下几种解决方案: 1.确保我们正确地实例化了"window"对象。我们需要检查我们的代码,看看我们是否正确地创建了"window"对象并将其设置为"appDelegate"的属性。 2.检查我们的代码,确保我们没有在"appDelegate"类中手动添加了一个名为"window"的方法。有时候我们可能会错误地将一个成员变量声明为一个方法,导致这个错误的发生。 3.如果我们通过Storyboard或XIB文件创建了窗口对象,我们需要确保我们正确地将窗口对象与"appDelegate"关联起来。可以通过检查我们的Storyboard或XIB文件中的连接和引用关系来解决这个问题。 总结一下,当我们遇到"appDelegate window: unrecognized selector sent to instance"错误时,需要检查我们的代码,确保我们正确地实例化了窗口对象,并且没有使用一个未定义的方法。 ### 回答2: 这个错误通常是由于在代码中调用了`[appDelegate window]`方法,但是`appDelegate`对象并不存在该方法所导致的。该方法的作用是返回`AppDelegate`对象的窗口属性。 产生此错误的原因可能有: 1. 在调用`[appDelegate window]`方法之前,没有正确初始化和分配内存给`appDelegate`对象。 2. `appDelegate`类中没有定义`window`属性或对应的`getter`方法。 解决此问题的方法是: 1. 确保在使用`[appDelegate window]`方法之前正确初始化和分配内存给`appDelegate`对象,可以使用`alloc init`等方法。 2. 确保`appDelegate`类中定义了`window`属性并有对应的`getter`方法。 以下是一个示例代码,演示了正确初始化`appDelegate`对象并调用`window`属性: ```objective-c // 创建并初始化AppDelegate对象 AppDelegate *appDelegate = [[AppDelegate alloc] init]; // 使用appDelegate对象的window属性 UIWindow *window = [appDelegate window]; ``` 希望以上回答能够解决你的问题。如果有任何进一步的问题,请随时提问。 ### 回答3: 这个错误通常是因为在使用iOS开发中的AppDelegate时调用了window方法,但是实际上AppDelegate类并没有该方法,导致了这个错误。 解决这个问题有几种方法: 1. 检查调用window方法的地方是否正确。确保你正在调用的对象确实是AppDelegate的实例,并且确保没有拼写错误。 2. 检查你的AppDelegate类是否正确实现了UIApplicationDelegate协议。确认你的AppDelegate类中有正确的UIApplicationDelegate方法实现,包括window属性的设置。 3. 检查你的Storyboard或XIB文件是否正确设置了AppDelegate的窗口。确认你的Storyboard或XIB文件中已经正确设置了AppDelegate的窗口,以便在应用程序启动时正确地加载窗口。 如果你仔细检查并尝试了以上方法,仍然无法解决这个错误,那么可能是由于其他原因引起的。这种情况下,你可以尝试删除并重新创建AppDelegate类,或者重新创建项目。如果问题仍然存在,那么可能是其他代码或框架中的错误导致的,需要进一步调试定位。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值