NSObject+AvoidCrash.m 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. //
  2. // NSObject+AvoidCrash.m
  3. // https://github.com/chenfanfang/AvoidCrash
  4. //
  5. // Created by mac on 16/10/11.
  6. // Copyright © 2016年 chenfanfang. All rights reserved.
  7. //
  8. #import "NSObject+AvoidCrash.h"
  9. #import "AvoidCrash.h"
  10. #import "AvoidCrashStubProxy.h"
  11. @implementation NSObject (AvoidCrash)
  12. + (void)avoidCrashExchangeMethodIfDealWithNoneSel:(BOOL)ifDealWithNoneSel {
  13. static dispatch_once_t onceToken;
  14. dispatch_once(&onceToken, ^{
  15. //setValue:forKey:
  16. [AvoidCrash exchangeInstanceMethod:[self class] method1Sel:@selector(setValue:forKey:) method2Sel:@selector(avoidCrashSetValue:forKey:)];
  17. //setValue:forKeyPath:
  18. [AvoidCrash exchangeInstanceMethod:[self class] method1Sel:@selector(setValue:forKeyPath:) method2Sel:@selector(avoidCrashSetValue:forKeyPath:)];
  19. //setValue:forUndefinedKey:
  20. [AvoidCrash exchangeInstanceMethod:[self class] method1Sel:@selector(setValue:forUndefinedKey:) method2Sel:@selector(avoidCrashSetValue:forUndefinedKey:)];
  21. //setValuesForKeysWithDictionary:
  22. [AvoidCrash exchangeInstanceMethod:[self class] method1Sel:@selector(setValuesForKeysWithDictionary:) method2Sel:@selector(avoidCrashSetValuesForKeysWithDictionary:)];
  23. //unrecognized selector sent to instance
  24. if (ifDealWithNoneSel) {
  25. [AvoidCrash exchangeInstanceMethod:[self class] method1Sel:@selector(methodSignatureForSelector:) method2Sel:@selector(avoidCrashMethodSignatureForSelector:)];
  26. [AvoidCrash exchangeInstanceMethod:[self class] method1Sel:@selector(forwardInvocation:) method2Sel:@selector(avoidCrashForwardInvocation:)];
  27. }
  28. });
  29. }
  30. //=================================================================
  31. // unrecognized selector sent to instance
  32. //=================================================================
  33. #pragma mark - unrecognized selector sent to instance
  34. static NSMutableArray *noneSelClassStrings;
  35. static NSMutableArray *noneSelClassStringPrefixs;
  36. + (void)setupNoneSelClassStringsArr:(NSArray<NSString *> *)classStrings {
  37. if (noneSelClassStrings){
  38. NSString *warningMsg = [NSString stringWithFormat:@"\n\n%@\n\n[AvoidCrash setupNoneSelClassStringsArr:];\n调用一此即可,多次调用会自动忽略后面的调用\n\n%@\n\n",AvoidCrashSeparatorWithFlag,AvoidCrashSeparator];
  39. AvoidCrashLog(@"%@",warningMsg);
  40. }
  41. static dispatch_once_t onceToken;
  42. dispatch_once(&onceToken, ^{
  43. noneSelClassStrings = [NSMutableArray array];
  44. for (NSString *className in classStrings) {
  45. if ([className hasPrefix:@"UI"] == NO &&
  46. [className isEqualToString:NSStringFromClass([NSObject class])] == NO) {
  47. [noneSelClassStrings addObject:className];
  48. } else {
  49. NSString *warningMsg = [NSString stringWithFormat:@"\n\n%@\n\n[AvoidCrash setupNoneSelClassStringsArr:];\n会忽略UI开头的类和NSObject类(请使用NSObject的子类)\n\n%@\n\n",AvoidCrashSeparatorWithFlag,AvoidCrashSeparator];
  50. AvoidCrashLog(@"%@",warningMsg);
  51. }
  52. }
  53. });
  54. }
  55. /**
  56. * 初始化一个需要防止”unrecognized selector sent to instance”的崩溃的类名前缀的数组
  57. */
  58. + (void)setupNoneSelClassStringPrefixsArr:(NSArray<NSString *> *)classStringPrefixs {
  59. if (noneSelClassStringPrefixs) {
  60. NSString *warningMsg = [NSString stringWithFormat:@"\n\n%@\n\n[AvoidCrash setupNoneSelClassStringPrefixsArr:];\n调用一此即可,多次调用会自动忽略后面的调用\n\n%@\n\n",AvoidCrashSeparatorWithFlag,AvoidCrashSeparator];
  61. AvoidCrashLog(@"%@",warningMsg);
  62. }
  63. static dispatch_once_t onceToken;
  64. dispatch_once(&onceToken, ^{
  65. noneSelClassStringPrefixs = [NSMutableArray array];
  66. for (NSString *classNamePrefix in classStringPrefixs) {
  67. if ([classNamePrefix hasPrefix:@"UI"] == NO &&
  68. [classNamePrefix hasPrefix:@"NS"] == NO) {
  69. [noneSelClassStringPrefixs addObject:classNamePrefix];
  70. } else {
  71. 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];
  72. AvoidCrashLog(@"%@",warningMsg);
  73. }
  74. }
  75. });
  76. }
  77. - (NSMethodSignature *)avoidCrashMethodSignatureForSelector:(SEL)aSelector {
  78. NSMethodSignature *ms = [self avoidCrashMethodSignatureForSelector:aSelector];
  79. BOOL flag = NO;
  80. if (ms == nil) {
  81. for (NSString *classStr in noneSelClassStrings) {
  82. if ([self isKindOfClass:NSClassFromString(classStr)]) {
  83. ms = [AvoidCrashStubProxy instanceMethodSignatureForSelector:@selector(proxyMethod)];
  84. flag = YES;
  85. break;
  86. }
  87. }
  88. }
  89. if (flag == NO) {
  90. NSString *selfClass = NSStringFromClass([self class]);
  91. for (NSString *classStrPrefix in noneSelClassStringPrefixs) {
  92. if ([selfClass hasPrefix:classStrPrefix]) {
  93. ms = [AvoidCrashStubProxy instanceMethodSignatureForSelector:@selector(proxyMethod)];
  94. }
  95. }
  96. }
  97. return ms;
  98. }
  99. - (void)avoidCrashForwardInvocation:(NSInvocation *)anInvocation {
  100. @try {
  101. [self avoidCrashForwardInvocation:anInvocation];
  102. } @catch (NSException *exception) {
  103. NSString *defaultToDo = AvoidCrashDefaultIgnore;
  104. [AvoidCrash noteErrorWithException:exception defaultToDo:defaultToDo];
  105. } @finally {
  106. }
  107. }
  108. //=================================================================
  109. // setValue:forKey:
  110. //=================================================================
  111. #pragma mark - setValue:forKey:
  112. - (void)avoidCrashSetValue:(id)value forKey:(NSString *)key {
  113. @try {
  114. [self avoidCrashSetValue:value forKey:key];
  115. }
  116. @catch (NSException *exception) {
  117. NSString *defaultToDo = AvoidCrashDefaultIgnore;
  118. [AvoidCrash noteErrorWithException:exception defaultToDo:defaultToDo];
  119. }
  120. @finally {
  121. }
  122. }
  123. //=================================================================
  124. // setValue:forKeyPath:
  125. //=================================================================
  126. #pragma mark - setValue:forKeyPath:
  127. - (void)avoidCrashSetValue:(id)value forKeyPath:(NSString *)keyPath {
  128. @try {
  129. [self avoidCrashSetValue:value forKeyPath:keyPath];
  130. }
  131. @catch (NSException *exception) {
  132. NSString *defaultToDo = AvoidCrashDefaultIgnore;
  133. [AvoidCrash noteErrorWithException:exception defaultToDo:defaultToDo];
  134. }
  135. @finally {
  136. }
  137. }
  138. //=================================================================
  139. // setValue:forUndefinedKey:
  140. //=================================================================
  141. #pragma mark - setValue:forUndefinedKey:
  142. - (void)avoidCrashSetValue:(id)value forUndefinedKey:(NSString *)key {
  143. @try {
  144. [self avoidCrashSetValue:value forUndefinedKey:key];
  145. }
  146. @catch (NSException *exception) {
  147. NSString *defaultToDo = AvoidCrashDefaultIgnore;
  148. [AvoidCrash noteErrorWithException:exception defaultToDo:defaultToDo];
  149. }
  150. @finally {
  151. }
  152. }
  153. //=================================================================
  154. // setValuesForKeysWithDictionary:
  155. //=================================================================
  156. #pragma mark - setValuesForKeysWithDictionary:
  157. - (void)avoidCrashSetValuesForKeysWithDictionary:(NSDictionary<NSString *,id> *)keyedValues {
  158. @try {
  159. [self avoidCrashSetValuesForKeysWithDictionary:keyedValues];
  160. }
  161. @catch (NSException *exception) {
  162. NSString *defaultToDo = AvoidCrashDefaultIgnore;
  163. [AvoidCrash noteErrorWithException:exception defaultToDo:defaultToDo];
  164. }
  165. @finally {
  166. }
  167. }
  168. @end