线上的崩溃对于APP来说是致命性的,但应用崩溃的原因有很多,比如方法找不到,数组越界等等

先解决 方法找不到 unrecognized selector sent to instance 而产生的崩溃吧。
说到实现原理,首先需要了解一个问题:一个对象收到一个无法响应的方法到程序崩溃,这之间发生了什么?
答:当一个方法找不到的时候,会走拦截调用和消息转发流程。
大致流程是:
1、对象调用方法时,系统会先调用该类的 +resolveInstanceMethod: 方法进行判断,如果返回 YES, 则表示能接受消息,NO 表示不能接受消息并进入第下步。
2、系统会调用该类的 -forwardingTargetForSelector:,在这个方法中,可以把这个消息转发给其他对象。敲黑板,这里是重点。
3、如果第二部返回 nil,那么首先它会发送 -methodSignatureForSelector: 消息,获得函数的参数和返回值类型。如果 -methodSignatureForSelector: 返回nil,Runtime则会发出 -doesNotRecognizeSelector: 消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime 就会创建一个 NSInvocation 对象并发送 -forwardInvocation:消息给目标对象。
1、给 NSObject 添加一个分类,再在这个分类中自定义一个方法,-my_forwardingTargetForSelector:,在 +load 方法中,交换 NSObject 的 forwardingTargetForSelector: 和 my_forwardingTargetForSelector:
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originalMethod = class_getInstanceMethod([NSObject class], @selector(forwardingTargetForSelector:));
Method swizzledMethod = class_getInstanceMethod([NSObject class], @selector(my_forwardingTargetForSelector:));
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
2、在自定义的方法中,先判断当前对象是否已经实现了消息转发方法,如果没有实现,就动态创建一个类,给这个类动态添加一个方法,再把消息转发给这个动态创建的方法,这样就不会崩溃了。
- (id)my_forwardingTargetForSelector:(SEL)aSelector {
// 获取NSObject的消息转发方法
SEL sel = NSSelectorFromString(@"forwardingTargetForSelector:");
Method method = class_getInstanceMethod(NSClassFromString(@"NSObject"), sel);
// 获取当前类的消息转发方法
Method _m = class_getInstanceMethod([self class],sel);
// 类本身有没有实现消息转发流程
BOOL transmit = method_getImplementation(_m) == method_getImplementation(method);
// 有木有实现下一步消息转发流程
if (transmit) {
// 判断有没有实现第三步消息转发
SEL sel1 = NSSelectorFromString(@"methodSignatureForSelector:");
Method method1 = class_getInstanceMethod(NSClassFromString(@"NSObject"), sel1);
Method _m1 = class_getInstanceMethod([self class], sel1);
transmit = method_getImplementation(_m1) == method_getImplementation(method1);
if (transmit) {
// 创建一个新类
NSString *errClassName = NSStringFromClass([self class]);
NSString *errSel = NSStringFromSelector(aSelector);
NSLog(@"出问题的类,出问题的方法 == %@ %@", errClassName, errSel);
NSString *className = @"CrachClass";
Class cls = NSClassFromString(className);
/// 如果类不存在 动态创建一个类
if (!cls) {
Class superCls = [NSObject class];
cls = objc_allocateClassPair(superCls, className.UTF8String, 0);
/// 给类添加方法
class_addMethod(cls, aSelector, (IMP)Crash, "@@:@");
objc_registerClassPair(cls);
}
/// 如果类没有对应的方法,则动态添加一个
if (!class_getInstanceMethod(NSClassFromString(className), aSelector)) {
class_addMethod(cls, aSelector, (IMP)Crash, "@@:@");
}
/// 把消息转发到当前动态生成类的实例上
return [[NSClassFromString(className) alloc] init];
}
}
return [self my_forwardingTargetForSelector:aSelector];
}
static int Crash(id slf, SEL selector) {
return 0;
}
主要是注意两点:
1:类有没有实现 forwardingTargetForSelector: 函数;
2:类有没有实现 methodSignatureForSelector: 函数,实现了就表明类实现了消息转发流程,那么不处理这个类。
还有一个常见的崩溃就是数组越界 index 2 beyond bounds [0 .. 1],对于这一类方法,方法是把系统的方法和自定义的方法交换,在自定义的方法里判断是否越界。
根据上面的思路,给 NSArray 添加分类,交换 NSArray 的 objectAtIndex: 方法。
然鹅,但是,然并暖,程序还是崩溃了,信息如下:
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndexedSubscript:]: index 2 beyond bounds [0 .. 1]'
仔细查看崩溃日志,终于发现问题了:-[__NSArrayI objectAtIndexedSubscript:]。这里是调用了 __NSArrayI 这个类的 -objectAtIndexedSubscript: 方法,而不是 NSArray 的方法。再来一次,具体代码如下:
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//不可变数组
Class __NSArrayI = NSClassFromString(@"__NSArrayI");
Method originalMethod = class_getInstanceMethod(__NSArrayI, @selector(objectAtIndexedSubscript:));
Method swizzledMethod = class_getInstanceMethod(__NSArrayI, @selector(my_objectAtIndexedSubscript:));
method_exchangeImplementations(originalMethod, swizzledMethod);
//可变数组
Class __NSArrayM = NSClassFromString(@"__NSArrayM");
Method oldMuTableMethod = class_getInstanceMethod(__NSArrayM, @selector(objectAtIndexedSubscript:));
Method newMuTableMethod = class_getInstanceMethod(__NSArrayM, @selector(my_objectMuTableAtIndexedSubscript:));
method_exchangeImplementations(oldMuTableMethod, newMuTableMethod);
});
}
- (id)my_objectAtIndexedSubscript:(NSUInteger)idx {
if (idx < 0 || idx >= self.count) {
@try {
return [self my_objectAtIndexedSubscript:idx];
} @catch (NSException *exception) {
NSLog(@"数组越界了~~~~~%@",exception.reason);
return nil;
}
}else{
return [self my_objectAtIndexedSubscript:idx];
}
}
- (id)my_objectMuTableAtIndexedSubscript:(NSUInteger)idx {
if (idx < 0 || idx >= self.count) {
@try {
return [self my_objectMuTableAtIndexedSubscript:idx];
} @catch (NSException *exception) {
NSLog(@"数组越界了~~~~~%@",exception.reason);
return nil;
}
}else{
return [self my_objectMuTableAtIndexedSubscript:idx];
}
}
还有其他的奔溃,以后更新
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的
是否可以在应用程序中包含的gem代码中知道应用程序的Rails文件系统根目录?这是gem来源的示例:moduleMyGemdefself.included(base)putsRails.root#returnnilendendActionController::Base.send:include,MyGem谢谢,抱歉我的英语不好 最佳答案 我发现解决类似问题的解决方案是使用railtie初始化程序包含我的模块。所以,在你的/lib/mygem/railtie.rbmoduleMyGemclassRailtie使用此代码,您的模块将在