常见运行时错误排查方式¶
目前运行时错误分几个大类情况,本教程会放几个常见案例来说明问题所在,并附上会触发代码模式
一.ObjC/Swift 对象的+load函数穿透问题¶
下面演示一些程序执行时序不对引起运行时错误的案例**
案例1. +load执行顺序引起字符串解密没有触发引起问题
Note
//MyAClass.m
@implementation MyAClass
+ (void)load {
[MyBClass show];
}
@end
//MybClass.m
@implementation MyBClass
+ (void)show {
NSString* msg = @"你好";
NSLog(msg);
}
@end
上面这段代码如果这个MyAClass.m/MyBClass.m 文件的I标识打开,将会被翻译成下面的样子,一般没有问题
Note
//MyAClass.m
@implementation MyAClass
+ (void)load {
[self performSelector:@selector(load_delay) withObject:self afterDelay:0 inModes:@[NSRunLoopCommonModes, NSDefaultRunLoopMode]];//延迟到main启动时候调用
}
+ (void)load_delay {
[MyBClass show];
}
@end
//MyBClass.m
char* decode_str1[] = {0x11,0x11,...};
__attribute__((constructor)) __decode_string()
{
...//完成decode_str1解码
}
@implementation MyBClass
+ (void)show {
NSString* msg = [NSString stringWithCString:decode_str1];
NSLog(msg);
}
@end
Warning
但是如果MyAClass.m上的I标识没有了,混淆工具就无法正确处理,改写+load,这就会导致load的执行时机比MyBClass的__decode_string 函数更早别调用,这时候NSLog打印的就是nil
案例2. +load执行顺序引起资源加载问题
Note
//MyAClass.m
@implementation MyAClass
+ (void)load {
[MyBClass show];
}
@end
//MybClass.m
@implementation MyBClass
+ (void)show {
UIImage* image = [UIImage imageNamed:@"nihao"];
NSAssert(image != nil, @"not found nihao");
}
@end
上面这段代码如果这个MyAClass.m/MyBClass.m 文件的I标识打开,将会被翻译成下面的样子,一般没有问题
Note
//MyAClass.m
@implementation MyAClass
+ (void)load {
[self performSelector:@selector(load_delay) withObject:self afterDelay:0 inModes:@[NSRunLoopCommonModes, NSDefaultRunLoopMode]];//延迟到main启动时候调用
}
+ (void)load_delay {
[MyBClass show];
}
@end
//MyBClass.m
@implementation MyBClass
+ (void)show {
UIImage* image = [UIImage imageNamed:@"nihao"];
NSAssert(image != nil, @"not found nihao");
}
@end
Warning
但是如果MyAClass.m上的I标识没有了,也是一样的问题,+load函数的执行时机可能比COSDK.framework启动时序更早UIImage.hook_imageNamed这个方法都还没有挂上去,这是时候一定引起运行时崩溃
二.函数拦截和API引起的问题¶
下面将会演示一些函数API没有被勾住的情况
案例1. 资源被改名加密,但是COSDK.framework运行时根本无法处理
Note
#import <SVGAImage.framework>
@implementation MyClass
-(void)getSVGAImage {
SVGAImage* img = [SVGAImage imageWithName:@"hello"];
NSAssert(img != nil, @"not found hello.svga");
}
@end
Warning
由于这个SVGAImage.framework用到的函数都不在拦截列表里面 API拦截列表,特特别是没有用ObjC运行时方法,所以也没法在COSDK.framework初始化的时候完成拦截操作。如果首先确认这个SVGAImage.framework时静态库架构,并且是用了C API还是可以通过修改这个framework完成拦截的混淆外部SDK存档
案例2. 第三方framework胡乱拦截导致YYModel解析失败
Note
#import <TencentApi.framework>
@implementation MyClass
-(void)getModel {
UserModel* user = [UserModel yy_modelWithJson:@{@"user" : @"haha}];
NSAssert(user != nil, @"not found user");
}
@end
Warning
当这个TencentApi.framework把YYModel相关代码打包进去了,就会引起解析失败,这个时候,我们只能通过排除解决问题 排除混淆设置
三.ObjC/Swift Metadata和资源映射机制引起的崩溃问题¶
这种就非常常见了,一般情况下当有外部资源文件的时候,会进行关联操作,如果ObjC/Swift对象被更换了,但是对应的资源文件没有被修改,会一定会出现问题
案例1. 关闭了xib、storyboard的C标识,引起运行时错误
一般情况下不要随便关闭xib、storyboard等外部文件上的C标识,资源文件的修改都是要做关联修改的
案例2. 自定义格式的资源引用了某些对象名字
Note
@implementation MyClass
-(void)getModel {
NSString* name = [NSString stringWithContentOfFile:@"nihao.cfg"];
Class cls = NSClassFromString(name);
...
}
@end
Warning
这种情况下最好的方法就是不去混淆这个nihao.cfg内部的类,可以在混淆配置中排除掉 排除混淆设置
四.ObjC/Swift 字符串编译期共用引起的问题¶
这类问题的处理关键就是要通过调试来确认问题所在,要么排除要么统一改名操作 案例1. 编译器对字符串做合并操作,共用了MyClass这个字符串引起的问题
Note
//MyAClass.m
@implementation MyClass
-(void)getModel {
UIImage* img = [UIImage imageNamed:@"MyClass"];
NSAssert(img != nil, @"not found MyClass");
}
@end
Warning
要重现这个错误,不能打开字符串加密功能,因为加密后MyClass就不会共用了, 同时也不要改名那个叫做MyClass.png的图片,如果同时开启了图片改名,那么这个问题就不存在了
五.Debug/Release行为不一致引起的问题¶
这类问题大部分都是某些工程有差异化引起的,这时候只能用日志来确认问题所在,然后缩小调试范围在Xcode中调试
还是无法确认问题所在¶
这时候最好方式就是提供一个案例给我们的技术支持人员,我们将能快速发现和修复问题,给你发一个临时定制版本!