OC防犯越界崩溃策略整理
来源:互联网 发布:centos创建文件夹命令 编辑:程序博客网 时间:2024/05/21 02:52
写在前面
OC上常见崩溃一般不亚于 数组越界以及字典设置为nil。
虽然大家基本都知道这些情况下,程序会表示抱歉,我要崩溃的,但是大多数情况下传入进来的都是一个变量,变量真正的值有时候就会正在的出乎程序员的意料,比如过大导致越界,或是尽然是空的。如果说这种意外情况无法避免,那么只能从侧面采取保护措施。
我先整理下常见的崩溃方法:
NSArray: objectAtIndex:NSMutableArray: addObject: insertObject:atIndex: removeObjectAtIndex: replaceObjectAtIndex:withObject:NSMutableDictionary: setObject:forKey:
- 取值:index超出array的索引范围
- 添加:插入的object为nil或者Null
- 插入:index大于count、插入的object为nil或者Null
- 删除:index超出array的索引范围
- 替换:index超出array的索引范围、替换的object为nil或者Null
现在市面上主流的两种结局方法是:
- 使用runtime hook系统的这几个方法
- 使用分类的形式重新封装一层方法
两种方法当然各有利弊,hack方便,但是风险不可控,使用分类的方式反锁,风险较易控制。
捕捉到错误之后,可以使用Crashlytics上传崩溃日志,这样就可以自我捕捉这些错误而不让系统抛出exception而崩溃。
个人推荐还是使用稳妥的方法,用分类重新封装一层方法的形式。
代替exception使用日志系统上报错误
日志上报使用的是fabric,有关fabric的使用,自行去google,这里不做过多讲解。
关键技术是使用fabric的方法recordCustomExceptionName:reason:frameArray:
/** * This method can be used to record a single exception structure in a report. This is particularly useful * when your code interacts with non-native languages like Lua, C#, or Javascript. This call can be * expensive and should only be used shortly before process termination. This API is not intended be to used * to log NSException objects. All safely-reportable NSExceptions are automatically captured by * Crashlytics. * * @param name The name of the custom exception * @param reason The reason this exception occured * @param frameArray An array of CLSStackFrame objects */- (void)recordCustomExceptionName:(NSString *)name reason:(nullable NSString *)reason frameArray:(CLS_GENERIC_NSARRAY(CLSStackFrame *) *)frameArray;
注释大致就是说可以自行发送日志到fabric,所以就有了下面的使用方法:
static void Log(NSString *fmt, ...) { NSMutableString *reportStr = [NSMutableString string]; va_list ap; va_start(ap, fmt); NSString *content = [[NSString alloc] initWithFormat:fmt arguments:ap]; [reportStr appendString:@"***Terminating app due to uncaught exception\r\n"]; [reportStr appendFormat:@"***reason:-%@\r\n", content]; va_end(ap); [reportStr appendFormat:@"*** First throw call stack:\n%@", [NSThread callStackSymbols]]; NSArray<NSString *> *callStackSymbols = [NSThread callStackSymbols]; NSMutableArray<CLSStackFrame *> *stackFrames = [NSMutableArray arrayWithCapacity:callStackSymbols.count]; for (NSString *callStachSymbol in callStackSymbols) { [stackFrames addObject:[CLSStackFrame stackFrameWithSymbol:callStachSymbol]]; }#ifdef DEBUG [NSException raise:NSInvalidArgumentException format:@"***reason%@", content];#else [CrashlyticsKit recordCustomExceptionName:@"自捕获崩溃" reason:content frameArray:stackFrames];#endif YXLogError(@"%@", reportStr);}
最后fabirc上捕捉到的日志就是non-fatals
使用RunTime hack系统方法的形式实现
工具类继承至NSObject,在load类方法中实现hack技术,分别hook住相应的方法
+ (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // NSArray [self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayI") originalSelector:@selector(objectAtIndex:) swizzledMethod:@selector(safeObjectAtIndex:)]; // NSMutableArray [self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayM") originalSelector:@selector(objectAtIndex:) swizzledMethod:@selector(safeObjectAtIndex:)]; // [self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayM") originalSelector:@selector(addObject:) swizzledMethod:@selector(safeAddObject:)]; [self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayM") originalSelector:@selector(replaceObjectAtIndex:withObject:) swizzledMethod:@selector(safeReplaceObjectAtIndex:withObject:)]; [self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayM") originalSelector:@selector(insertObject:atIndex:) swizzledMethod:@selector(safeInsertObject:atIndex:)]; // NSDictionary // [self swizzleClassMethodWithClass:[NSDictionary class] originalSelector:@selector(dictionaryWithObjects:forKeys:count:) swizzledMethod:@selector(safeDictionaryWithObjects:forKeys:count:)]; // NSMutableDictionary [self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSDictionaryM") originalSelector:@selector(setObject:forKey:) swizzledMethod:@selector(safeSetObject:forKey:)]; });}
NSArray
@interface NSArray (Safte)@end@implementation NSArray (Safte)- (id)safeObjectAtIndex:(NSUInteger)index { @autoreleasepool { if (self.count > index) { return [self safeObjectAtIndex:index]; }else { Log(@"[%@ %@] index %lu beyond bounds [0 .. %lu]", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)index, MAX((unsigned long)self.count - 1, 0)); return nil; } }}@end
NSMutableArray
@interface NSMutableArray (Safte)@end@implementation NSMutableArray (Safte)- (id)safeObjectAtIndex:(NSUInteger)index { @autoreleasepool { if (self.count > index) { return [self safeObjectAtIndex:index]; }else { Log(@"[%@ %@] index %lu beyond bounds [0 .. %lu]", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)index, MAX((unsigned long)self.count - 1, 0)); return nil; } }}- (void)safeAddObject:(id)anObject { @autoreleasepool { if (anObject) { [self safeAddObject:anObject]; }else { Log(@"[%@ %@], nil object. object cannot be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); } }}- (void)safeReplaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject { @autoreleasepool { if (index >= self.count) { Log(@"[%@ %@] index %lu beyond bounds [0 .. %lu].", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)index, MAX((unsigned long)self.count - 1, 0)); return; }else if (!anObject) { Log(@"[%@ %@] nil object. object cannot be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); return; } [self safeReplaceObjectAtIndex:index withObject:anObject]; }}- (void)safeInsertObject:(id)anObject atIndex:(NSUInteger)index { @autoreleasepool { if (index > self.count) { Log(@"[%@ %@] index %lu beyond bounds [0...%lu].", NSStringFromClass([self class]), NSStringFromSelector(_cmd), (unsigned long)index, MAX((unsigned long)self.count - 1, 0)); return; } if (!anObject) { Log(@"[%@ %@] nil object. object cannot be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); return; } [self safeInsertObject:anObject atIndex:index]; }}@end
NSDictionary
@interface NSDictionary (Safte)@end@implementation NSDictionary (Safte)+ (instancetype)safeDictionaryWithObjects:(const id _Nonnull __unsafe_unretained *)objects forKeys:(const id<NSCopying> _Nonnull __unsafe_unretained *)keys count:(NSUInteger)cnt { @autoreleasepool { id validObjects[cnt]; id<NSCopying> validKeys[cnt]; NSUInteger count = 0; for (NSUInteger i = 0; i < cnt; i++) { if (objects[i] && keys[i]) { validObjects[count] = objects[i]; validKeys[count] = keys[i]; count ++; } else { Log(@"[%@ %@] NIL object or key at index%lu.", NSStringFromClass(self), NSStringFromSelector(_cmd), (unsigned long)i); } } return [self safeDictionaryWithObjects:validObjects forKeys:validKeys count:count]; }}@end
NSMuatbleDictionary
@interface NSMutableDictionary (Safte)@end@implementation NSMutableDictionary (Safte)- (void)safeSetObject:(id)anObject forKey:(id<NSCopying>)aKey { @autoreleasepool { if (!aKey) { Log(@"[%@ %@] nil key. key cannot be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); return; } if (!anObject) { Log(@"[%@ %@] nil object. object cannot be nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); return; } [self safeSetObject:anObject forKey:aKey]; }}@end
安全容器方法
分装一层分类,规定以后团队都使用这个分类方法
NSArray
@implementation NSArray (Safety)- (id)at:(NSUInteger)i { if (i < self.count) { return self[i]; } else { Log(@"NSArray objectAtIndex beyond the bound: %lu is beyond the count:%lu!", (unsigned long)i, (unsigned long)self.count); return nil; }}+ (instancetype)create:(id)firstObj, ... { NSMutableArray *array = [NSMutableArray new]; va_list args; va_start(args, firstObj); for (id arg = firstObj; arg != nil; arg = va_arg(args, id)) { [array add:arg]; } va_end(args); if ([self isEqual:NSMutableDictionary.class]) { return array; } else { return [array copy]; }}@end
NSDictionary
@implementation NSDictionary (Safety)+ (instancetype)kv:(id)firstObj, ... { NSMutableDictionary *dic = [NSMutableDictionary new]; va_list args; va_start(args, firstObj); id key, value; int i = 0; for (id arg = firstObj; arg != nil; arg = va_arg(args, id)) { if (i % 2 == 0) { key = arg; } else { value = arg; [dic key:key value:value]; } i++; } va_end(args); if ([self isEqual:NSMutableDictionary.class]) { return dic; } else { return dic.copy; }}@end
NSMutableArray
@implementation NSMutableArray (Safety)- (void)add:(id)object { if (object == nil) { Log(@"NSMutableArray added a nil object!"); return; } [self addObject:object];}- (void)insert:(id)o at:(NSUInteger)i { if (o == nil) { Log(@"NSMutableArray inserted a nil object!"); } else if (i > self.count) { //这里得是> 而不能>=,因为insert能插入到最后一个 Log(@"NSArray insertObject:atIndex: beyond the bound: %lu is beyond the count:%lu!", (unsigned long)i, (unsigned long)self.count); } else { [self insertObject:o atIndex:i]; }}- (void)removeAt:(NSUInteger)i { if (i < self.count) { [self removeObjectAtIndex:i]; } else { Log(@"NSArray removeObjectAtIndex beyond the bound: %lu is beyond the count:%lu!", (unsigned long)i, (unsigned long)self.count); }}@end
NSMutableDictionary
@implementation NSMutableDictionary (Safety)- (void)key:(id)k value:(id)v { if (k == nil || ![k conformsToProtocol:@protocol(NSCopying)]) { Log(@"NSMutableDictionary setObject:forKey:, key %@ is invalid!", k); return; } self[k] = v;}@end
阅读全文
0 0
- OC防犯越界崩溃策略整理
- 数组越界导致程序崩溃
- alloc/越界存取/free 与 进程崩溃
- 内存访问越界在哪里崩溃
- Memcpy越界操作导致free崩溃分析
- 由memcpy越界引起的崩溃
- OC : NSException (崩溃)
- OC整理
- OC整理
- android app防被杀策略
- Linux进程防杀/防崩溃monitor的实现
- Linux进程防杀/防崩溃monitor的实现
- memset内存越界导致函数堆栈崩溃的例子
- Objective-c防止数组越界而崩溃(全局效果)
- 越界
- 旅游防骗攻略[整理]
- OC基础理论知识整理
- oc的(课件整理)
- Gym 101243 I Land Division[计算几何]
- VMwareWorkstation10 中安装Centos6.5
- Java技术体系的四大平台(SE ,EE,ME,Card)
- MFC 程序如何使用 printf 输出调试信息
- [Python]网络爬虫(八):糗事百科的网络爬虫(v0.3)源码及解析(简化更新)
- OC防犯越界崩溃策略整理
- java语言的跨平台
- 10016
- linux 系统安全命令
- MVC模式
- 非空格式验证框架--Validation的使用
- html中文乱码怎么解决?
- CodeForces 1C(计算几何)
- 疯狂JAVA讲义-接口和抽象类比较