// // NSObject+AvoidCrash.m // https://github.com/chenfanfang/AvoidCrash // // Created by mac on 16/10/11. // Copyright © 2016年 chenfanfang. All rights reserved. // #import "NSObject+AvoidCrash.h" #import "AvoidCrash.h" #import "AvoidCrashStubProxy.h" @implementation NSObject (AvoidCrash) + (void)avoidCrashExchangeMethodIfDealWithNoneSel:(BOOL)ifDealWithNoneSel { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //setValue:forKey: [AvoidCrash exchangeInstanceMethod:[self class] method1Sel:@selector(setValue:forKey:) method2Sel:@selector(avoidCrashSetValue:forKey:)]; //setValue:forKeyPath: [AvoidCrash exchangeInstanceMethod:[self class] method1Sel:@selector(setValue:forKeyPath:) method2Sel:@selector(avoidCrashSetValue:forKeyPath:)]; //setValue:forUndefinedKey: [AvoidCrash exchangeInstanceMethod:[self class] method1Sel:@selector(setValue:forUndefinedKey:) method2Sel:@selector(avoidCrashSetValue:forUndefinedKey:)]; //setValuesForKeysWithDictionary: [AvoidCrash exchangeInstanceMethod:[self class] method1Sel:@selector(setValuesForKeysWithDictionary:) method2Sel:@selector(avoidCrashSetValuesForKeysWithDictionary:)]; //unrecognized selector sent to instance if (ifDealWithNoneSel) { [AvoidCrash exchangeInstanceMethod:[self class] method1Sel:@selector(methodSignatureForSelector:) method2Sel:@selector(avoidCrashMethodSignatureForSelector:)]; [AvoidCrash exchangeInstanceMethod:[self class] method1Sel:@selector(forwardInvocation:) method2Sel:@selector(avoidCrashForwardInvocation:)]; } }); } //================================================================= // unrecognized selector sent to instance //================================================================= #pragma mark - unrecognized selector sent to instance static NSMutableArray *noneSelClassStrings; static NSMutableArray *noneSelClassStringPrefixs; + (void)setupNoneSelClassStringsArr:(NSArray *)classStrings { if (noneSelClassStrings) { NSString *warningMsg = [NSString stringWithFormat:@"\n\n%@\n\n[AvoidCrash setupNoneSelClassStringsArr:];\n调用一此即可,多次调用会自动忽略后面的调用\n\n%@\n\n",AvoidCrashSeparatorWithFlag,AvoidCrashSeparator]; AvoidCrashLog(@"%@",warningMsg); } static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ noneSelClassStrings = [NSMutableArray array]; for (NSString *className in classStrings) { if ([className hasPrefix:@"UI"] == NO && [className isEqualToString:NSStringFromClass([NSObject class])] == NO) { [noneSelClassStrings addObject:className]; } else { NSString *warningMsg = [NSString stringWithFormat:@"\n\n%@\n\n[AvoidCrash setupNoneSelClassStringsArr:];\n会忽略UI开头的类和NSObject类(请使用NSObject的子类)\n\n%@\n\n",AvoidCrashSeparatorWithFlag,AvoidCrashSeparator]; AvoidCrashLog(@"%@",warningMsg); } } }); } /** * 初始化一个需要防止”unrecognized selector sent to instance”的崩溃的类名前缀的数组 */ + (void)setupNoneSelClassStringPrefixsArr:(NSArray *)classStringPrefixs { if (noneSelClassStringPrefixs) { NSString *warningMsg = [NSString stringWithFormat:@"\n\n%@\n\n[AvoidCrash setupNoneSelClassStringPrefixsArr:];\n调用一此即可,多次调用会自动忽略后面的调用\n\n%@\n\n",AvoidCrashSeparatorWithFlag,AvoidCrashSeparator]; AvoidCrashLog(@"%@",warningMsg); } static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ noneSelClassStringPrefixs = [NSMutableArray array]; for (NSString *classNamePrefix in classStringPrefixs) { if ([classNamePrefix hasPrefix:@"UI"] == NO && [classNamePrefix hasPrefix:@"NS"] == NO) { [noneSelClassStringPrefixs addObject:classNamePrefix]; } else { NSString *warningMsg = [NSString stringWithFormat:@"\n\n%@\n\n[AvoidCrash setupNoneSelClassStringsArr:];\n会忽略UI开头的类和NS开头的类\n若需要对NS开头的类防止”unrecognized selector sent to instance”(比如NSArray),请使用setupNoneSelClassStringsArr:\n\n%@\n\n",AvoidCrashSeparatorWithFlag,AvoidCrashSeparator]; AvoidCrashLog(@"%@",warningMsg); } } }); } - (NSMethodSignature *)avoidCrashMethodSignatureForSelector:(SEL)aSelector { NSMethodSignature *ms = [self avoidCrashMethodSignatureForSelector:aSelector]; BOOL flag = NO; if (ms == nil) { for (NSString *classStr in noneSelClassStrings) { if ([self isKindOfClass:NSClassFromString(classStr)]) { ms = [AvoidCrashStubProxy instanceMethodSignatureForSelector:@selector(proxyMethod)]; flag = YES; break; } } } if (flag == NO) { NSString *selfClass = NSStringFromClass([self class]); for (NSString *classStrPrefix in noneSelClassStringPrefixs) { if ([selfClass hasPrefix:classStrPrefix]) { ms = [AvoidCrashStubProxy instanceMethodSignatureForSelector:@selector(proxyMethod)]; } } } return ms; } - (void)avoidCrashForwardInvocation:(NSInvocation *)anInvocation { @try { [self avoidCrashForwardInvocation:anInvocation]; } @catch (NSException *exception) { NSString *defaultToDo = AvoidCrashDefaultIgnore; [AvoidCrash noteErrorWithException:exception defaultToDo:defaultToDo]; } @finally { } } //================================================================= // setValue:forKey: //================================================================= #pragma mark - setValue:forKey: - (void)avoidCrashSetValue:(id)value forKey:(NSString *)key { @try { [self avoidCrashSetValue:value forKey:key]; } @catch (NSException *exception) { NSString *defaultToDo = AvoidCrashDefaultIgnore; [AvoidCrash noteErrorWithException:exception defaultToDo:defaultToDo]; } @finally { } } //================================================================= // setValue:forKeyPath: //================================================================= #pragma mark - setValue:forKeyPath: - (void)avoidCrashSetValue:(id)value forKeyPath:(NSString *)keyPath { @try { [self avoidCrashSetValue:value forKeyPath:keyPath]; } @catch (NSException *exception) { NSString *defaultToDo = AvoidCrashDefaultIgnore; [AvoidCrash noteErrorWithException:exception defaultToDo:defaultToDo]; } @finally { } } //================================================================= // setValue:forUndefinedKey: //================================================================= #pragma mark - setValue:forUndefinedKey: - (void)avoidCrashSetValue:(id)value forUndefinedKey:(NSString *)key { @try { [self avoidCrashSetValue:value forUndefinedKey:key]; } @catch (NSException *exception) { NSString *defaultToDo = AvoidCrashDefaultIgnore; [AvoidCrash noteErrorWithException:exception defaultToDo:defaultToDo]; } @finally { } } //================================================================= // setValuesForKeysWithDictionary: //================================================================= #pragma mark - setValuesForKeysWithDictionary: - (void)avoidCrashSetValuesForKeysWithDictionary:(NSDictionary *)keyedValues { @try { [self avoidCrashSetValuesForKeysWithDictionary:keyedValues]; } @catch (NSException *exception) { NSString *defaultToDo = AvoidCrashDefaultIgnore; [AvoidCrash noteErrorWithException:exception defaultToDo:defaultToDo]; } @finally { } } @end