222 lines
8.0 KiB
Objective-C
222 lines
8.0 KiB
Objective-C
//
|
|
// 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<NSString *> *)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<NSString *> *)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<NSString *,id> *)keyedValues {
|
|
@try {
|
|
[self avoidCrashSetValuesForKeysWithDictionary:keyedValues];
|
|
}
|
|
@catch (NSException *exception) {
|
|
NSString *defaultToDo = AvoidCrashDefaultIgnore;
|
|
[AvoidCrash noteErrorWithException:exception defaultToDo:defaultToDo];
|
|
}
|
|
@finally {
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@end
|