Commit f48d9566 by 刘卓鑫

防止crash

parent 80ef91a1
// ++ /dev/null
//
// ZXChooseSchoolViewController.h
// ColorfulSchool
//
// Created by liuZX on 2018/7/25.
// Copyright © 2018年 Colorful Any Door. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "ZXNewSchool.h"
typedef void(^selectSchool)(ZXNewCampus *campus);
@interface ZXChooseSchoolViewController : UIViewController
@property (nonatomic, copy) selectSchool select;
- (instancetype) initWithSchoolName:(selectSchool) selectSchool;
@end
// ++ /dev/null
//
// ZXRegistorViewController.h
// ColorfulSchool
//
// Created by liuZX on 2018/7/25.
// Copyright © 2018年 Colorful Any Door. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ZXRegistorViewController : UIViewController
@end
//
//
// UserInfoViewController.h
// ColorfulSchool
//
// Created by Listen on 2017/8/10.
// Copyright © 2017年 Colorful Any Door. All rights reserved.
// 老版本分流
#import <UIKit/UIKit.h>
#import "UserCenterModel.h"
@interface Old_UserInfoViewController : UIViewController
@property (nonatomic)UserCenerModelItem * userCenterItem;
@end
../../../JJException/JJException/Source/Main/JJException.h
\ No newline at end of file
../../../JJException/JJException/Source/Main/JJExceptionMacros.h
\ No newline at end of file
../../../JJException/JJException/Source/Main/JJExceptionProxy.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSArray+ArrayHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSAttributedString+AttributedStringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSDictionary+DictionaryHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSMutableArray+MutableArrayHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSMutableAttributedString+MutableAttributedStringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSMutableDictionary+MutableDictionaryHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSMutableSet+MutableSetHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSMutableString+MutableStringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSNotificationCenter+ClearNotification.h
\ No newline at end of file
../../../JJException/JJException/Source/DeallocBlock/NSObject+DeallocBlock.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSObject+KVOCrash.h
\ No newline at end of file
../../../JJException/JJException/Source/Swizzle/NSObject+SwizzleHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSObject+UnrecognizedSelectorHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSObject+ZombieHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSSet+SetHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSString+StringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSTimer+CleanTimer.h
\ No newline at end of file
../../../JJException/JJException/Source/Main/JJException.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSArray+ArrayHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSAttributedString+AttributedStringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSDictionary+DictionaryHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSMutableArray+MutableArrayHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSMutableAttributedString+MutableAttributedStringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSMutableDictionary+MutableDictionaryHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSMutableSet+MutableSetHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSMutableString+MutableStringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSNotificationCenter+ClearNotification.h
\ No newline at end of file
../../../JJException/JJException/Source/DeallocBlock/NSObject+DeallocBlock.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSObject+KVOCrash.h
\ No newline at end of file
../../../JJException/JJException/Source/Swizzle/NSObject+SwizzleHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSObject+UnrecognizedSelectorHook.h
\ No newline at end of file
../../../JJException/JJException/Source/MRC/NSObject+ZombieHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSSet+SetHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSString+StringHook.h
\ No newline at end of file
../../../JJException/JJException/Source/ARC/NSTimer+CleanTimer.h
\ No newline at end of file
//
// NSAttributedString+AttributedStringHook.h
// JJException
//
// Created by Jezz on 2018/9/20.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSAttributedString (AttributedStringHook)
+ (void)jj_swizzleNSAttributedString;
@end
//
// NSAttributedString+AttributedStringHook.m
// JJException
//
// Created by Jezz on 2018/9/20.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSAttributedString+AttributedStringHook.h"
#import "NSObject+SwizzleHook.h"
#import <objc/runtime.h>
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSAttributedString_AttributedStringHook)
@implementation NSAttributedString (AttributedStringHook)
+ (void)jj_swizzleNSAttributedString{
NSAttributedString* instanceObject = [NSAttributedString new];
Class cls = object_getClass(instanceObject);
swizzleInstanceMethod(cls, @selector(initWithString:), @selector(hookInitWithString:));
swizzleInstanceMethod(cls, @selector(attributedSubstringFromRange:), @selector(hookAttributedSubstringFromRange:));
swizzleInstanceMethod(cls, @selector(attribute:atIndex:effectiveRange:), @selector(hookAttribute:atIndex:effectiveRange:));
swizzleInstanceMethod(cls, @selector(enumerateAttribute:inRange:options:usingBlock:), @selector(hookEnumerateAttribute:inRange:options:usingBlock:));
swizzleInstanceMethod(cls, @selector(enumerateAttributesInRange:options:usingBlock:), @selector(hookEnumerateAttributesInRange:options:usingBlock:));
}
- (id)hookInitWithString:(NSString*)str{
if (str){
return [self hookInitWithString:str];
}
handleCrashException(JJExceptionGuardNSStringContainer,@"NSAttributedString initWithString parameter nil");
return nil;
}
- (id)hookAttribute:(NSAttributedStringKey)attrName atIndex:(NSUInteger)location effectiveRange:(nullable NSRangePointer)range{
if (location < self.length){
return [self hookAttribute:attrName atIndex:location effectiveRange:range];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSAttributedString attribute:atIndex:effectiveRange: attrName:%@ location:%tu",attrName,location]);
return nil;
}
- (NSAttributedString *)hookAttributedSubstringFromRange:(NSRange)range{
if (range.location + range.length <= self.length) {
return [self hookAttributedSubstringFromRange:range];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSAttributedString attributedSubstringFromRange range:%@",NSStringFromRange(range)]);
return nil;
}
- (void)hookEnumerateAttribute:(NSString *)attrName inRange:(NSRange)range options:(NSAttributedStringEnumerationOptions)opts usingBlock:(void (^)(id _Nullable, NSRange, BOOL * _Nonnull))block{
if (range.location + range.length <= self.length) {
[self hookEnumerateAttribute:attrName inRange:range options:opts usingBlock:block];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSAttributedString enumerateAttribute attrName:%@ range:%@",attrName,NSStringFromRange(range)]);
}
}
- (void)hookEnumerateAttributesInRange:(NSRange)range options:(NSAttributedStringEnumerationOptions)opts usingBlock:(void (^)(NSDictionary<NSString*,id> * _Nonnull, NSRange, BOOL * _Nonnull))block{
if (range.location + range.length <= self.length) {
[self hookEnumerateAttributesInRange:range options:opts usingBlock:block];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSAttributedString enumerateAttributesInRange range:%@",NSStringFromRange(range)]);
}
}
@end
//
// NSMutableAttributedString+MutableAttributedStringHook.h
// JJException
//
// Created by Jezz on 2018/9/20.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSMutableAttributedString (MutableAttributedStringHook)
+ (void)jj_swizzleNSMutableAttributedString;
@end
//
// NSMutableAttributedString+MutableAttributedStringHook.m
// JJException
//
// Created by Jezz on 2018/9/20.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSMutableAttributedString+MutableAttributedStringHook.h"
#import "NSObject+SwizzleHook.h"
#import <objc/runtime.h>
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSMutableAttributedString_MutableAttributedStringHook)
@implementation NSMutableAttributedString (MutableAttributedStringHook)
+ (void)jj_swizzleNSMutableAttributedString{
NSMutableAttributedString* instanceObject = [NSMutableAttributedString new];
Class cls = object_getClass(instanceObject);
swizzleInstanceMethod(cls,@selector(initWithString:), @selector(hookInitWithString:));
swizzleInstanceMethod(cls,@selector(initWithString:attributes:), @selector(hookInitWithString:attributes:));
swizzleInstanceMethod(cls,@selector(addAttribute:value:range:), @selector(hookAddAttribute:value:range:));
swizzleInstanceMethod(cls,@selector(addAttributes:range:), @selector(hookAddAttributes:range:));
swizzleInstanceMethod(cls,@selector(setAttributes:range:), @selector(hookSetAttributes:range:));
swizzleInstanceMethod(cls,@selector(removeAttribute:range:), @selector(hookRemoveAttribute:range:));
swizzleInstanceMethod(cls,@selector(deleteCharactersInRange:), @selector(hookDeleteCharactersInRange:));
swizzleInstanceMethod(cls,@selector(replaceCharactersInRange:withString:), @selector(hookReplaceCharactersInRange:withString:));
swizzleInstanceMethod(cls,@selector(replaceCharactersInRange:withAttributedString:), @selector(hookReplaceCharactersInRange:withAttributedString:));
}
- (id)hookInitWithString:(NSString*)str{
if (str){
return [self hookInitWithString:str];
}
handleCrashException(JJExceptionGuardNSStringContainer,@"NSMutableAttributedString initWithString parameter nil");
return nil;
}
- (id)hookInitWithString:(NSString*)str attributes:(nullable NSDictionary*)attributes{
if (str){
return [self hookInitWithString:str attributes:attributes];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString initWithString:attributes: str:%@ attributes:%@",str,attributes]);
return nil;
}
- (void)hookAddAttribute:(id)name value:(id)value range:(NSRange)range{
if (!range.length) {
[self hookAddAttribute:name value:value range:range];
}else if (value){
if (range.location + range.length <= self.length) {
[self hookAddAttribute:name value:value range:range];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString addAttribute:value:range: name:%@ value:%@ range:%@",name,value,NSStringFromRange(range)]);
}
}else {
handleCrashException(JJExceptionGuardNSStringContainer,@"NSMutableAttributedString addAttribute:value:range: value nil");
}
}
- (void)hookAddAttributes:(NSDictionary<NSString *,id> *)attrs range:(NSRange)range{
if (!range.length) {
[self hookAddAttributes:attrs range:range];
}else if (attrs){
if (range.location + range.length <= self.length) {
[self hookAddAttributes:attrs range:range];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString addAttributes:range: attrs:%@ range:%@",attrs,NSStringFromRange(range)]);
}
}else{
handleCrashException(JJExceptionGuardNSStringContainer,@"NSMutableAttributedString addAttributes:range: value nil");
}
}
- (void)hookSetAttributes:(NSDictionary<NSString *,id> *)attrs range:(NSRange)range{
if (!range.length) {
[self hookSetAttributes:attrs range:range];
}else if (attrs){
if (range.location + range.length <= self.length) {
[self hookSetAttributes:attrs range:range];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString setAttributes:range: attrs:%@ range:%@",attrs,NSStringFromRange(range)]);
}
}else{
handleCrashException(JJExceptionGuardNSStringContainer,@"NSMutableAttributedString setAttributes:range: attrs nil");
}
}
- (void)hookRemoveAttribute:(id)name range:(NSRange)range {
if (!range.length) {
[self hookRemoveAttribute:name range:range];
}else if (name){
if (range.location + range.length <= self.length) {
[self hookRemoveAttribute:name range:range];
}else {
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString removeAttribute:range: name:%@ range:%@",name,NSStringFromRange(range)]);
}
}else{
handleCrashException(JJExceptionGuardNSStringContainer,@"NSMutableAttributedString removeAttribute:range: attrs nil");
}
}
- (void)hookDeleteCharactersInRange:(NSRange)range {
if (range.location + range.length <= self.length) {
[self hookDeleteCharactersInRange:range];
}else {
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString deleteCharactersInRange: range:%@",NSStringFromRange(range)]);
}
}
- (void)hookReplaceCharactersInRange:(NSRange)range withString:(NSString *)str {
if (str){
if (range.location + range.length <= self.length) {
[self hookReplaceCharactersInRange:range withString:str];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString replaceCharactersInRange:withString string:%@ range:%@",str,NSStringFromRange(range)]);
}
}else{
handleCrashException(JJExceptionGuardNSStringContainer,@"NSMutableAttributedString replaceCharactersInRange:withString: string nil");
}
}
- (void)hookReplaceCharactersInRange:(NSRange)range withAttributedString:(NSAttributedString *)str {
if (str){
if (range.location + range.length <= self.length) {
[self hookReplaceCharactersInRange:range withAttributedString:str];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableAttributedString replaceCharactersInRange:withString string:%@ range:%@",str,NSStringFromRange(range)]);
}
}else{
handleCrashException(JJExceptionGuardNSStringContainer,@"NSMutableAttributedString replaceCharactersInRange:withString: attributedString nil");
}
}
@end
//
// NSMutableSet+MutableSetHook.h
// JJException
//
// Created by Jezz on 2018/11/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSMutableSet (MutableSetHook)
+ (void)jj_swizzleNSMutableSet;
@end
NS_ASSUME_NONNULL_END
//
// NSMutableSet+MutableSetHook.m
// JJException
//
// Created by Jezz on 2018/11/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSMutableSet+MutableSetHook.h"
#import "NSObject+SwizzleHook.h"
#import <objc/runtime.h>
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSMutableSet_MutableSetHook)
@implementation NSMutableSet (MutableSetHook)
+ (void)jj_swizzleNSMutableSet{
NSMutableSet* instanceObject = [NSMutableSet new];
Class cls = object_getClass(instanceObject);
swizzleInstanceMethod(cls,@selector(addObject:), @selector(hookAddObject:));
swizzleInstanceMethod(cls,@selector(removeObject:), @selector(hookRemoveObject:));
}
- (void) hookAddObject:(id)object {
if (object) {
[self hookAddObject:object];
} else {
handleCrashException(JJExceptionGuardArrayContainer,@"NSSet addObject nil object");
}
}
- (void) hookRemoveObject:(id)object {
if (object) {
[self hookRemoveObject:object];
} else {
handleCrashException(JJExceptionGuardArrayContainer,@"NSSet removeObject nil object");
}
}
@end
//
// NSMutableString+MutableStringHook.h
// JJException
//
// Created by Jezz on 2018/9/18.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSMutableString (MutableStringHook)
+ (void)jj_swizzleNSMutableString;
@end
//
// NSMutableString+MutableStringHook.m
// JJException
//
// Created by Jezz on 2018/9/18.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSMutableString+MutableStringHook.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSMutableString_MutableStringHook)
@implementation NSMutableString (MutableStringHook)
+ (void)jj_swizzleNSMutableString{
//__NSCFString
swizzleInstanceMethod(NSClassFromString(@"__NSCFString"), @selector(appendString:), @selector(hookAppendString:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFString"), @selector(insertString:atIndex:), @selector(hookInsertString:atIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFString"), @selector(deleteCharactersInRange:), @selector(hookDeleteCharactersInRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFString"), @selector(substringFromIndex:), @selector(hookSubstringFromIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFString"), @selector(substringToIndex:), @selector(hookSubstringToIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFString"), @selector(substringWithRange:), @selector(hookSubstringWithRange:));
}
- (void) hookAppendString:(NSString *)aString{
if (aString){
[self hookAppendString:aString];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableString appendString value:%@ parameter nil",self]);
}
}
- (void) hookInsertString:(NSString *)aString atIndex:(NSUInteger)loc{
if (aString && loc <= self.length) {
[self hookInsertString:aString atIndex:loc];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableString insertString:atIndex: value:%@ paremeter string:%@ atIndex:%tu",self,aString,loc]);
}
}
- (void) hookDeleteCharactersInRange:(NSRange)range{
if (range.location + range.length <= self.length){
[self hookDeleteCharactersInRange:range];
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableString deleteCharactersInRange value:%@ range:%@",self,NSStringFromRange(range)]);
}
}
- (NSString *)hookSubstringFromIndex:(NSUInteger)from{
if (from <= self.length) {
return [self hookSubstringFromIndex:from];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableString substringFromIndex value:%@ from:%tu",self,from]);
return nil;
}
- (NSString *)hookSubstringToIndex:(NSUInteger)to{
if (to <= self.length) {
return [self hookSubstringToIndex:to];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableString substringToIndex value:%@ to:%tu",self,to]);
return self;
}
- (NSString *)hookSubstringWithRange:(NSRange)range{
if (range.location + range.length <= self.length) {
return [self hookSubstringWithRange:range];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSMutableString substringWithRange value:%@ range:%@",self,NSStringFromRange(range)]);
return nil;
}
@end
//
// NSNotificationCenter+ClearNotification.h
// JJException
//
// Created by Jezz on 2018/9/6.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSNotificationCenter (ClearNotification)
+ (void)jj_swizzleNSNotificationCenter;
@end
//
// NSNotificationCenter+ClearNotification.m
// JJException
//
// Created by Jezz on 2018/9/6.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSNotificationCenter+ClearNotification.h"
#import "NSObject+SwizzleHook.h"
#import "NSObject+DeallocBlock.h"
#import "JJExceptionMacros.h"
#import <objc/runtime.h>
JJSYNTH_DUMMY_CLASS(NSNotificationCenter_ClearNotification)
@implementation NSNotificationCenter (ClearNotification)
+ (void)jj_swizzleNSNotificationCenter{
[self jj_swizzleInstanceMethod:@selector(addObserver:selector:name:object:) withSwizzledBlock:^id(JJSwizzleObject *swizzleInfo) {
return ^(__unsafe_unretained id self,id observer,SEL aSelector,NSString* aName,id anObject){
[self processAddObserver:observer selector:aSelector name:aName object:anObject swizzleInfo:swizzleInfo];
};
}];
}
- (void)processAddObserver:(id)observer selector:(SEL)aSelector name:(NSNotificationName)aName object:(id)anObject swizzleInfo:(JJSwizzleObject*)swizzleInfo{
if (!observer) {
return;
}
if ([observer isKindOfClass:NSObject.class]) {
__unsafe_unretained typeof(observer) unsafeObject = observer;
[observer jj_deallocBlock:^{
[[NSNotificationCenter defaultCenter] removeObserver:unsafeObject];
}];
}
void(*originIMP)(__unsafe_unretained id,SEL,id,SEL,NSString*,id);
originIMP = (__typeof(originIMP))[swizzleInfo getOriginalImplementation];
if (originIMP != NULL) {
originIMP(self,swizzleInfo.selector,observer,aSelector,aName,anObject);
}
}
@end
//
// NSSet+SetHook.h
// JJException
//
// Created by Jezz on 2018/11/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSSet (SetHook)
+ (void)jj_swizzleNSSet;
@end
NS_ASSUME_NONNULL_END
//
// NSSet+SetHook.m
// JJException
//
// Created by Jezz on 2018/11/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSSet+SetHook.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSSet_SetHook)
@implementation NSSet (SetHook)
+ (void)jj_swizzleNSSet{
[NSSet jj_swizzleClassMethod:@selector(setWithObject:) withSwizzleMethod:@selector(hookSetWithObject:)];
}
+ (instancetype)hookSetWithObject:(id)object{
if (object){
return [self hookSetWithObject:object];
}
handleCrashException(JJExceptionGuardArrayContainer,@"NSSet setWithObject nil object");
return nil;
}
@end
//
// NSString+StringHook.h
// JJException
//
// Created by Jezz on 2018/9/18.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSString (StringHook)
+ (void)jj_swizzleNSString;
@end
//
// NSString+StringHook.m
// JJException
//
// Created by Jezz on 2018/9/18.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSString+StringHook.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSString_StringHook)
@implementation NSString (StringHook)
+ (void)jj_swizzleNSString{
[NSString jj_swizzleClassMethod:@selector(stringWithUTF8String:) withSwizzleMethod:@selector(hookStringWithUTF8String:)];
[NSString jj_swizzleClassMethod:@selector(stringWithCString:encoding:) withSwizzleMethod:@selector(hookStringWithCString:encoding:)];
//NSPlaceholderString
swizzleInstanceMethod(NSClassFromString(@"NSPlaceholderString"), @selector(initWithCString:encoding:), @selector(hookInitWithCString:encoding:));
swizzleInstanceMethod(NSClassFromString(@"NSPlaceholderString"), @selector(initWithString:), @selector(hookInitWithString:));
//_NSCFConstantString
swizzleInstanceMethod(NSClassFromString(@"__NSCFConstantString"), @selector(substringFromIndex:), @selector(hookSubstringFromIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFConstantString"), @selector(substringToIndex:), @selector(hookSubstringToIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFConstantString"), @selector(substringWithRange:), @selector(hookSubstringWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFConstantString"), @selector(rangeOfString:options:range:locale:), @selector(hookRangeOfString:options:range:locale:));
//NSTaggedPointerString
swizzleInstanceMethod(NSClassFromString(@"NSTaggedPointerString"), @selector(substringFromIndex:), @selector(hookSubstringFromIndex:));
swizzleInstanceMethod(NSClassFromString(@"NSTaggedPointerString"), @selector(substringToIndex:), @selector(hookSubstringToIndex:));
swizzleInstanceMethod(NSClassFromString(@"NSTaggedPointerString"), @selector(substringWithRange:), @selector(hookSubstringWithRange:));
swizzleInstanceMethod(NSClassFromString(@"NSTaggedPointerString"), @selector(rangeOfString:options:range:locale:), @selector(hookRangeOfString:options:range:locale:));
}
+ (NSString*) hookStringWithUTF8String:(const char *)nullTerminatedCString{
if (NULL != nullTerminatedCString) {
return [self hookStringWithUTF8String:nullTerminatedCString];
}
handleCrashException(JJExceptionGuardNSStringContainer,@"NSString stringWithUTF8String NULL char pointer");
return nil;
}
+ (nullable instancetype) hookStringWithCString:(const char *)cString encoding:(NSStringEncoding)enc
{
if (NULL != cString){
return [self hookStringWithCString:cString encoding:enc];
}
handleCrashException(JJExceptionGuardNSStringContainer,@"NSString stringWithCString:encoding: NULL char pointer");
return nil;
}
- (nullable instancetype) hookInitWithString:(id)cString{
if (nil != cString){
return [self hookInitWithString:cString];
}
handleCrashException(JJExceptionGuardNSStringContainer,@"NSString initWithString nil parameter");
return nil;
}
- (nullable instancetype) hookInitWithCString:(const char *)nullTerminatedCString encoding:(NSStringEncoding)encoding{
if (NULL != nullTerminatedCString){
return [self hookInitWithCString:nullTerminatedCString encoding:encoding];
}
handleCrashException(JJExceptionGuardNSStringContainer,@"NSString initWithCString:encoding NULL char pointer");
return nil;
}
- (NSString *)hookSubstringFromIndex:(NSUInteger)from{
if (from <= self.length) {
return [self hookSubstringFromIndex:from];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSString substringFromIndex value:%@ from:%tu",self,from]);
return nil;
}
- (NSString *)hookSubstringToIndex:(NSUInteger)to{
if (to <= self.length) {
return [self hookSubstringToIndex:to];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSString substringToIndex value:%@ from:%tu",self,to]);
return self;
}
- (NSString *)hookSubstringWithRange:(NSRange)range{
if (range.location + range.length <= self.length) {
return [self hookSubstringWithRange:range];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSString substringWithRange value:%@ range:%@",self,NSStringFromRange(range)]);
return nil;
}
- (NSRange)hookRangeOfString:(NSString *)searchString options:(NSStringCompareOptions)mask range:(NSRange)range locale:(nullable NSLocale *)locale{
if (searchString){
if (range.location + range.length <= self.length) {
return [self hookRangeOfString:searchString options:mask range:range locale:locale];
}
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSString rangeOfString:options:range:locale: value:%@ range:%@",self,NSStringFromRange(range)]);
return NSMakeRange(NSNotFound, 0);
}else{
handleCrashException(JJExceptionGuardNSStringContainer,[NSString stringWithFormat:@"NSString rangeOfString:options:range:locale: searchString nil value:%@ range:%@",self,NSStringFromRange(range)]);
return NSMakeRange(NSNotFound, 0);
}
}
@end
//
// NSTimer+CleanResource.h
// JJException
//
// Created by Jezz on 2018/9/4.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSTimer (CleanTimer)
+ (void)jj_swizzleNSTimer;
@end
//
// NSTimer+CleanResource.m
// JJException
//
// Created by Jezz on 2018/9/4.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSTimer+CleanTimer.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
/**
Copy the NSTimer Info
*/
@interface TimerObject : NSObject
@property(nonatomic,readwrite,assign)NSTimeInterval ti;
/**
weak reference target
*/
@property(nonatomic,readwrite,weak)id target;
@property(nonatomic,readwrite,assign)SEL selector;
@property(nonatomic,readwrite,assign)id userInfo;
/**
TimerObject Associated NSTimer
*/
@property(nonatomic,readwrite,weak)NSTimer* timer;
/**
Record the target class name
*/
@property(nonatomic,readwrite,copy)NSString* targetClassName;
/**
Record the target method name
*/
@property(nonatomic,readwrite,copy)NSString* targetMethodName;
@end
@implementation TimerObject
- (void)fireTimer{
if (!self.target) {
[self.timer invalidate];
self.timer = nil;
handleCrashException(JJExceptionGuardNSTimer,[NSString stringWithFormat:@"Need invalidate timer from target:%@ method:%@",self.targetClassName,self.targetMethodName]);
return;
}
if ([self.target respondsToSelector:self.selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self.target performSelector:self.selector withObject:self.timer];
#pragma clang diagnostic pop
}
}
@end
@implementation NSTimer (CleanTimer)
+ (void)jj_swizzleNSTimer{
swizzleClassMethod([NSTimer class], @selector(scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:), @selector(hookScheduledTimerWithTimeInterval:target:selector:userInfo:repeats:));
}
+ (NSTimer*)hookScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo{
if (!yesOrNo) {
return [self hookScheduledTimerWithTimeInterval:ti target:aTarget selector:aSelector userInfo:userInfo repeats:yesOrNo];
}
TimerObject* timerObject = [TimerObject new];
timerObject.ti = ti;
timerObject.target = aTarget;
timerObject.selector = aSelector;
timerObject.userInfo = userInfo;
if (aTarget) {
timerObject.targetClassName = [NSString stringWithCString:object_getClassName(aTarget) encoding:NSASCIIStringEncoding];
}
timerObject.targetMethodName = NSStringFromSelector(aSelector);
NSTimer* timer = [NSTimer hookScheduledTimerWithTimeInterval:ti target:timerObject selector:@selector(fireTimer) userInfo:userInfo repeats:yesOrNo];
timerObject.timer = timer;
return timer;
}
@end
//
// NSObject+DeallocBlock.h
// JJException
//
// Created by Jezz on 2018/9/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSObject (DeallocBlock)
/**
Observer current instance class dealloc action
@param block dealloc callback
*/
- (void)jj_deallocBlock:(void(^)(void))block;
@end
//
// NSObject+DeallocBlock.m
// JJException
//
// Created by Jezz on 2018/9/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSObject+DeallocBlock.h"
#import <objc/runtime.h>
static const char DeallocNSObjectKey;
/**
Observer the target middle object
*/
@interface DeallocStub : NSObject
@property (nonatomic,readwrite,copy) void(^deallocBlock)(void);
@end
@implementation DeallocStub
- (void)dealloc {
if (self.deallocBlock) {
self.deallocBlock();
}
self.deallocBlock = nil;
}
@end
@implementation NSObject (DeallocBlock)
- (void)jj_deallocBlock:(void(^)(void))block{
@synchronized(self){
NSMutableArray* blockArray = objc_getAssociatedObject(self, &DeallocNSObjectKey);
if (!blockArray) {
blockArray = [NSMutableArray array];
objc_setAssociatedObject(self, &DeallocNSObjectKey, blockArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
DeallocStub *stub = [DeallocStub new];
stub.deallocBlock = block;
[blockArray addObject:stub];
}
}
@end
//
// NSArray+ArrayHook.h
// JJException
//
// Created by Jezz on 2018/7/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSArray (ArrayHook)
+ (void)jj_swizzleNSArray;
@end
//
// NSArray+ArrayHook.m
// JJException
//
// Created by Jezz on 2018/7/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSArray+ArrayHook.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSArray_ArrayHook)
@implementation NSArray (ArrayHook)
+ (void)jj_swizzleNSArray{
[NSArray jj_swizzleClassMethod:@selector(arrayWithObject:) withSwizzleMethod:@selector(hookArrayWithObject:)];
[NSArray jj_swizzleClassMethod:@selector(arrayWithObjects:count:) withSwizzleMethod:@selector(hookArrayWithObjects:count:)];
/* __NSArray0 */
swizzleInstanceMethod(NSClassFromString(@"__NSArray0"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSArray0"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSArray0"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
/* __NSArrayI */
swizzleInstanceMethod(NSClassFromString(@"__NSArrayI"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayI"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayI"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
/* __NSArrayI_Transfer */
swizzleInstanceMethod(NSClassFromString(@"__NSArrayI_Transfer"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayI_Transfer"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayI_Transfer"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
/* above iOS10 __NSSingleObjectArrayI */
swizzleInstanceMethod(NSClassFromString(@"__NSSingleObjectArrayI"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSSingleObjectArrayI"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSSingleObjectArrayI"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
/* __NSFrozenArrayM */
swizzleInstanceMethod(NSClassFromString(@"__NSFrozenArrayM"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSFrozenArrayM"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSFrozenArrayM"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
/* __NSArrayReversed */
swizzleInstanceMethod(NSClassFromString(@"__NSArrayReversed"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayReversed"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayReversed"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
}
+ (instancetype) hookArrayWithObject:(id)anObject
{
if (anObject) {
return [self hookArrayWithObject:anObject];
}
handleCrashException(JJExceptionGuardArrayContainer,@"NSArray arrayWithObject object is nil");
return nil;
}
- (id) hookObjectAtIndex:(NSUInteger)index {
if (index < self.count) {
return [self hookObjectAtIndex:index];
}
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSArray objectAtIndex invalid index:%tu total:%tu",index,self.count]);
return nil;
}
- (id) hookObjectAtIndexedSubscript:(NSInteger)index {
if (index < self.count) {
return [self hookObjectAtIndexedSubscript:index];
}
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSArray objectAtIndexedSubscript invalid index:%tu total:%tu",index,self.count]);
return nil;
}
- (NSArray *)hookSubarrayWithRange:(NSRange)range
{
if (range.location + range.length <= self.count){
return [self hookSubarrayWithRange:range];
}else if (range.location < self.count){
return [self hookSubarrayWithRange:NSMakeRange(range.location, self.count-range.location)];
}
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSArray subarrayWithRange invalid range location:%tu length:%tu",range.location,range.length]);
return nil;
}
+ (instancetype)hookArrayWithObjects:(const id [])objects count:(NSUInteger)cnt
{
NSInteger index = 0;
id objs[cnt];
for (NSInteger i = 0; i < cnt ; ++i) {
if (objects[i]) {
objs[index++] = objects[i];
}else{
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSArray arrayWithObjects invalid index object:%tu total:%tu",i,cnt]);
}
}
return [self hookArrayWithObjects:objs count:index];
}
@end
//
// NSDictionary+DictionaryHook.h
// JJException
//
// Created by Jezz on 2018/7/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSDictionary (DictionaryHook)
+ (void)jj_swizzleNSDictionary;
@end
//
// NSDictionary+DictionaryHook.m
// JJException
//
// Created by Jezz on 2018/7/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSDictionary+DictionaryHook.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSDictionary_DictionaryHook)
@implementation NSDictionary (DictionaryHook)
+ (void)jj_swizzleNSDictionary{
[NSDictionary jj_swizzleClassMethod:@selector(dictionaryWithObject:forKey:) withSwizzleMethod:@selector(hookDictionaryWithObject:forKey:)];
[NSDictionary jj_swizzleClassMethod:@selector(dictionaryWithObjects:forKeys:count:) withSwizzleMethod:@selector(hookDictionaryWithObjects:forKeys:count:)];
}
+ (instancetype) hookDictionaryWithObject:(id)object forKey:(id)key
{
if (object && key) {
return [self hookDictionaryWithObject:object forKey:key];
}
handleCrashException(JJExceptionGuardDictionaryContainer,[NSString stringWithFormat:@"NSDictionary dictionaryWithObject invalid object:%@ and key:%@",object,key]);
return nil;
}
+ (instancetype) hookDictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(NSUInteger)cnt
{
NSInteger index = 0;
id ks[cnt];
id objs[cnt];
for (NSInteger i = 0; i < cnt ; ++i) {
if (keys[i] && objects[i]) {
ks[index] = keys[i];
objs[index] = objects[i];
++index;
}else{
handleCrashException(JJExceptionGuardDictionaryContainer,[NSString stringWithFormat:@"NSDictionary dictionaryWithObjects invalid keys:%@ and object:%@",keys[i],objects[i]]);
}
}
return [self hookDictionaryWithObjects:objs forKeys:ks count:index];
}
@end
//
// NSMutableArray+MutableArrayHook.h
// JJException
//
// Created by Jezz on 2018/7/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSMutableArray (MutableArrayHook)
+ (void)jj_swizzleNSMutableArray;
@end
//
// NSMutableArray+MutableArrayHook.m
// JJException
//
// Created by Jezz on 2018/7/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSMutableArray+MutableArrayHook.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSMutableArray_MutableArrayHook)
@implementation NSMutableArray (MutableArrayHook)
+ (void)jj_swizzleNSMutableArray{
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(addObject:), @selector(hookAddObject:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(insertObject:atIndex:), @selector(hookInsertObject:atIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(removeObjectAtIndex:), @selector(hookRemoveObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(replaceObjectAtIndex:withObject:), @selector(hookReplaceObjectAtIndex:withObject:));
swizzleInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(removeObjectsInRange:), @selector(hookRemoveObjectsInRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(subarrayWithRange:), @selector(hookSubarrayWithRange:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(objectAtIndexedSubscript:), @selector(hookObjectAtIndexedSubscript:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(addObject:), @selector(hookAddObject:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(insertObject:atIndex:), @selector(hookInsertObject:atIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(removeObjectAtIndex:), @selector(hookRemoveObjectAtIndex:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(replaceObjectAtIndex:withObject:), @selector(hookReplaceObjectAtIndex:withObject:));
swizzleInstanceMethod(NSClassFromString(@"__NSCFArray"), @selector(removeObjectsInRange:), @selector(hookRemoveObjectsInRange:));
}
- (void) hookAddObject:(id)anObject {
if (anObject) {
[self hookAddObject:anObject];
}else{
handleCrashException(JJExceptionGuardArrayContainer,@"NSMutableArray addObject nil object");
}
}
- (id) hookObjectAtIndex:(NSUInteger)index {
if (index < self.count) {
return [self hookObjectAtIndex:index];
}
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSMutableArray objectAtIndex invalid index:%tu total:%tu",index,self.count]);
return nil;
}
- (id) hookObjectAtIndexedSubscript:(NSInteger)index {
if (index < self.count) {
return [self hookObjectAtIndexedSubscript:index];
}
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSMutableArray objectAtIndexedSubscript invalid index:%tu total:%tu",index,self.count]);
return nil;
}
- (void) hookInsertObject:(id)anObject atIndex:(NSUInteger)index {
if (anObject && index <= self.count) {
[self hookInsertObject:anObject atIndex:index];
}else{
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSMutableArray insertObject invalid index:%tu total:%tu insert object:%@",index,self.count,anObject]);
}
}
- (void) hookRemoveObjectAtIndex:(NSUInteger)index {
if (index < self.count) {
[self hookRemoveObjectAtIndex:index];
}else{
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSMutableArray removeObjectAtIndex invalid index:%tu total:%tu",index,self.count]);
}
}
- (void) hookReplaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
if (index < self.count && anObject) {
[self hookReplaceObjectAtIndex:index withObject:anObject];
}else{
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSMutableArray replaceObjectAtIndex invalid index:%tu total:%tu replace object:%@",index,self.count,anObject]);
}
}
- (void) hookRemoveObjectsInRange:(NSRange)range {
if (range.location + range.length <= self.count) {
[self hookRemoveObjectsInRange:range];
}else{
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSMutableArray removeObjectsInRange invalid range location:%tu length:%tu",range.location,range.length]);
}
}
- (NSArray *)hookSubarrayWithRange:(NSRange)range
{
if (range.location + range.length <= self.count){
return [self hookSubarrayWithRange:range];
}else if (range.location < self.count){
return [self hookSubarrayWithRange:NSMakeRange(range.location, self.count-range.location)];
}
handleCrashException(JJExceptionGuardArrayContainer,[NSString stringWithFormat:@"NSMutableArray subarrayWithRange invalid range location:%tu length:%tu",range.location,range.length]);
return nil;
}
@end
//
// NSMutableDictionary+MutableDictionaryHook.h
// JJException
//
// Created by Jezz on 2018/7/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSMutableDictionary (MutableDictionaryHook)
+ (void)jj_swizzleNSMutableDictionary;
@end
//
// NSMutableDictionary+MutableDictionaryHook.m
// JJException
//
// Created by Jezz on 2018/7/15.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSMutableDictionary+MutableDictionaryHook.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSMutableDictionary_MutableDictionaryHook)
@implementation NSMutableDictionary (MutableDictionaryHook)
+ (void)jj_swizzleNSMutableDictionary{
swizzleInstanceMethod(NSClassFromString(@"__NSDictionaryM"), @selector(setObject:forKey:), @selector(hookSetObject:forKey:));
swizzleInstanceMethod(NSClassFromString(@"__NSDictionaryM"), @selector(removeObjectForKey:), @selector(hookRemoveObjectForKey:));
}
- (void) hookSetObject:(id)object forKey:(id)key {
if (object && key) {
[self hookSetObject:object forKey:key];
}else{
handleCrashException(JJExceptionGuardDictionaryContainer,[NSString stringWithFormat:@"NSMutableDictionary setObject invalid object:%@ and key:%@",object,key],self);
}
}
- (void) hookRemoveObjectForKey:(id)key {
if (key) {
[self hookRemoveObjectForKey:key];
}else{
handleCrashException(JJExceptionGuardDictionaryContainer,@"NSMutableDictionary removeObjectForKey nil key",self);
}
}
@end
//
// NSObject+KVOCrash.h
// JJException
//
// Created by Jezz on 2018/8/29.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSObject (KVOCrash)
+ (void)jj_swizzleKVOCrash;
@end
//
// NSObject+KVOCrash.m
// JJException
//
// Created by Jezz on 2018/8/29.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSObject+KVOCrash.h"
#import "NSObject+SwizzleHook.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import "JJExceptionProxy.h"
static const char DeallocKVOKey;
static const char ObserverDeallocKVOKey;
/**
Record the kvo object
Override the isEqual and hash method
*/
@interface KVOObjectItem : NSObject
@property(nonatomic,readwrite,assign)NSObject* observer;
@property(nonatomic,readwrite,copy)NSString* keyPath;
@property(nonatomic,readwrite,assign)NSKeyValueObservingOptions options;
@property(nonatomic,readwrite,assign)void* context;
@end
@implementation KVOObjectItem
- (BOOL)isEqual:(KVOObjectItem*)object{
if ([self.observer isEqual:object.observer] && [self.keyPath isEqualToString:object.keyPath]) {
return YES;
}
return NO;
}
- (NSUInteger)hash{
return [self.observer hash] ^ [self.keyPath hash];
}
- (void)dealloc{
self.observer = nil;
self.context = nil;
if (self.keyPath) {
[self.keyPath release];
}
[super dealloc];
}
@end
@interface KVOObjectContainer : NSObject
/**
KVO object array set
*/
@property(nonatomic,readwrite,retain)NSMutableSet* kvoObjectSet;
/**
Associated owner object
*/
@property(nonatomic,readwrite,unsafe_unretained)NSObject* whichObject;
/**
NSMutableSet safe-thread
*/
#if OS_OBJECT_HAVE_OBJC_SUPPORT
@property(nonatomic,readwrite,retain)dispatch_semaphore_t kvoLock;
#else
@property(nonatomic,readwrite,assign)dispatch_semaphore_t kvoLock;
#endif
- (void)addKVOObjectItem:(KVOObjectItem*)item;
- (void)removeKVOObjectItem:(KVOObjectItem*)item;
- (BOOL)checkKVOItemExist:(KVOObjectItem*)item;
@end
@implementation KVOObjectContainer
- (void)addKVOObjectItem:(KVOObjectItem*)item{
if (item) {
dispatch_semaphore_wait(self.kvoLock, DISPATCH_TIME_FOREVER);
[self.kvoObjectSet addObject:item];
dispatch_semaphore_signal(self.kvoLock);
}
}
- (void)removeKVOObjectItem:(KVOObjectItem*)item{
if (item) {
dispatch_semaphore_wait(self.kvoLock, DISPATCH_TIME_FOREVER);
[self.kvoObjectSet removeObject:item];
dispatch_semaphore_signal(self.kvoLock);
}
}
- (BOOL)checkKVOItemExist:(KVOObjectItem*)item{
dispatch_semaphore_wait(self.kvoLock, DISPATCH_TIME_FOREVER);
BOOL exist = NO;
if (!item) {
dispatch_semaphore_signal(self.kvoLock);
return exist;
}
exist = [self.kvoObjectSet containsObject:item];
dispatch_semaphore_signal(self.kvoLock);
return exist;
}
- (dispatch_semaphore_t)kvoLock{
if (!_kvoLock) {
_kvoLock = dispatch_semaphore_create(1);
return _kvoLock;
}
return _kvoLock;
}
/**
Clean the kvo object array and temp var
release the dispatch_semaphore
*/
- (void)dealloc{
[self.kvoObjectSet release];
self.whichObject = nil;
dispatch_release(self.kvoLock);
[super dealloc];
}
- (void)cleanKVOData{
for (KVOObjectItem* item in self.kvoObjectSet) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
@try {
((void(*)(id,SEL,id,NSString*))objc_msgSend)(self.whichObject,@selector(hookRemoveObserver:forKeyPath:),item.observer,item.keyPath);
}@catch (NSException *exception) {
}
#pragma clang diagnostic pop
}
}
- (NSMutableSet*)kvoObjectSet{
if(_kvoObjectSet){
return _kvoObjectSet;
}
_kvoObjectSet = [[NSMutableSet alloc] init];
return _kvoObjectSet;
}
@end
@interface JJObserverContainer : NSObject
@property (nonatomic,readwrite,retain) NSHashTable* observers;
/**
Associated owner object
*/
@property(nonatomic,readwrite,assign) NSObject* whichObject;
- (void)addObserver:(KVOObjectItem *)observer;
- (void)removeObserver:(KVOObjectItem *)observer;
@end
@implementation JJObserverContainer
- (instancetype)init
{
self = [super init];
if (self) {
self.observers = [NSHashTable hashTableWithOptions:NSMapTableWeakMemory];
}
return self;
}
- (void)addObserver:(KVOObjectItem *)observer
{
@synchronized (self) {
[self.observers addObject:observer];
}
}
- (void)removeObserver:(KVOObjectItem *)observer
{
@synchronized (self) {
[self.observers removeObject:observer];
}
}
- (void)cleanObservers{
for (KVOObjectItem* item in self.observers) {
[self.whichObject removeObserver:item.observer forKeyPath:item.keyPath];
}
@synchronized (self) {
[self.observers removeAllObjects];
}
}
- (void)dealloc{
self.whichObject = nil;
[self.observers release];
[super dealloc];
}
@end
@implementation NSObject (KVOCrash)
+ (void)jj_swizzleKVOCrash{
swizzleInstanceMethod([self class], @selector(addObserver:forKeyPath:options:context:), @selector(hookAddObserver:forKeyPath:options:context:));
swizzleInstanceMethod([self class], @selector(removeObserver:forKeyPath:), @selector(hookRemoveObserver:forKeyPath:));
swizzleInstanceMethod([self class], @selector(removeObserver:forKeyPath:context:), @selector(hookRemoveObserver:forKeyPath:context:));
}
- (void)hookAddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
if ([self ignoreKVOInstanceClass:observer]) {
[self hookAddObserver:observer forKeyPath:keyPath options:options context:context];
return;
}
if (!observer || keyPath.length == 0) {
return;
}
KVOObjectContainer* objectContainer = objc_getAssociatedObject(self,&DeallocKVOKey);
KVOObjectItem* item = [[KVOObjectItem alloc] init];
item.observer = observer;
item.keyPath = keyPath;
item.options = options;
item.context = context;
if (!objectContainer) {
objectContainer = [KVOObjectContainer new];
[objectContainer setWhichObject:self];
objc_setAssociatedObject(self, &DeallocKVOKey, objectContainer, OBJC_ASSOCIATION_RETAIN);
[objectContainer release];
}
if (![objectContainer checkKVOItemExist:item]) {
[objectContainer addKVOObjectItem:item];
[self hookAddObserver:observer forKeyPath:keyPath options:options context:context];
}
JJObserverContainer* observerContainer = objc_getAssociatedObject(observer,&ObserverDeallocKVOKey);
if (!observerContainer) {
observerContainer = [JJObserverContainer new];
[observerContainer setWhichObject:self];
[observerContainer addObserver:item];
objc_setAssociatedObject(observer, &ObserverDeallocKVOKey, observerContainer, OBJC_ASSOCIATION_RETAIN);
[observerContainer release];
}else{
[observerContainer addObserver:item];
}
[item release];
jj_swizzleDeallocIfNeeded(self.class);
jj_swizzleDeallocIfNeeded(observer.class);
}
- (void)hookRemoveObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void*)context{
if ([self ignoreKVOInstanceClass:observer]) {
[self hookRemoveObserver:observer forKeyPath:keyPath context:context];
return;
}
[self removeObserver:observer forKeyPath:keyPath];
}
- (void)hookRemoveObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath{
if ([self ignoreKVOInstanceClass:observer]) {
[self hookRemoveObserver:observer forKeyPath:keyPath];
return;
}
KVOObjectContainer* objectContainer = objc_getAssociatedObject(self, &DeallocKVOKey);
if (!observer) {
return;
}
if (!objectContainer) {
return;
}
KVOObjectItem* item = [[KVOObjectItem alloc] init];
item.observer = observer;
item.keyPath = keyPath;
if ([objectContainer checkKVOItemExist:item]) {
@try {
[self hookRemoveObserver:observer forKeyPath:keyPath];
}@catch (NSException *exception) {
}
[objectContainer removeKVOObjectItem:item];
}
[item release];
}
/**
Ignore Special Library
@param object Instance Class
@return YES or NO
*/
- (BOOL)ignoreKVOInstanceClass:(id)object{
if (!object) {
return NO;
}
//Ignore ReactiveCocoa
if (object_getClass(object) == objc_getClass("RACKVOProxy")) {
return YES;
}
//Ignore AMAP
NSString* className = NSStringFromClass(object_getClass(object));
if ([className hasPrefix:@"AMap"]) {
return YES;
}
return NO;
}
/**
* Hook the kvo object dealloc and to clean the kvo array
*/
- (void)jj_cleanKVO{
KVOObjectContainer* objectContainer = objc_getAssociatedObject(self, &DeallocKVOKey);
JJObserverContainer* observerContainer = objc_getAssociatedObject(self, &ObserverDeallocKVOKey);
if (objectContainer) {
[objectContainer cleanKVOData];
}else if(observerContainer){
[observerContainer cleanObservers];
}
}
@end
//
// NSObject+UnrecognizedSelectorHook.h
// JJException
//
// Created by Jezz on 2018/7/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSObject (UnrecognizedSelectorHook)
+ (void)jj_swizzleUnrecognizedSelector;
@end
//
// NSObject+UnrecognizedSelectorHook.m
// JJException
//
// Created by Jezz on 2018/7/11.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSObject+UnrecognizedSelectorHook.h"
#import "NSObject+SwizzleHook.h"
#import <objc/runtime.h>
#import "JJExceptionProxy.h"
#import "JJExceptionMacros.h"
JJSYNTH_DUMMY_CLASS(NSObject_UnrecognizedSelectorHook)
@implementation NSObject (UnrecognizedSelectorHook)
+ (void)jj_swizzleUnrecognizedSelector{
//Class Method
swizzleClassMethod([self class], @selector(methodSignatureForSelector:), @selector(classMethodSignatureForSelectorSwizzled:));
swizzleClassMethod([self class], @selector(forwardInvocation:), @selector(forwardClassInvocationSwizzled:));
//Instance Method
swizzleInstanceMethod([self class], @selector(methodSignatureForSelector:), @selector(methodSignatureForSelectorSwizzled:));
swizzleInstanceMethod([self class], @selector(forwardInvocation:), @selector(forwardInvocationSwizzled:));
}
+ (NSMethodSignature*)classMethodSignatureForSelectorSwizzled:(SEL)aSelector {
NSMethodSignature* methodSignature = [self classMethodSignatureForSelectorSwizzled:aSelector];
if (methodSignature) {
return methodSignature;
}
return [self.class checkObjectSignatureAndCurrentClass:self.class];
}
- (NSMethodSignature*)methodSignatureForSelectorSwizzled:(SEL)aSelector {
NSMethodSignature* methodSignature = [self methodSignatureForSelectorSwizzled:aSelector];
if (methodSignature) {
return methodSignature;
}
return [self.class checkObjectSignatureAndCurrentClass:self.class];
}
/**
* Check the class method signature to the [NSObject class]
* If not equals,return nil
* If equals,return the v@:@ method
@param currentClass Class
@return NSMethodSignature
*/
+ (NSMethodSignature *)checkObjectSignatureAndCurrentClass:(Class)currentClass{
IMP originIMP = class_getMethodImplementation([NSObject class], @selector(methodSignatureForSelector:));
IMP currentClassIMP = class_getMethodImplementation(currentClass, @selector(methodSignatureForSelector:));
// If current class override methodSignatureForSelector return nil
if (originIMP != currentClassIMP){
return nil;
}
// Customer method signature
// void xxx(id,sel,id)
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
/**
Forward instance object
@param invocation NSInvocation
*/
- (void)forwardInvocationSwizzled:(NSInvocation*)invocation{
NSString* message = [NSString stringWithFormat:@"Unrecognized instance class:%@ and selector:%@",NSStringFromClass(self.class),NSStringFromSelector(invocation.selector)];
handleCrashException(JJExceptionGuardUnrecognizedSelector,message);
}
/**
Forward class object
@param invocation NSInvocation
*/
+ (void)forwardClassInvocationSwizzled:(NSInvocation*)invocation{
NSString* message = [NSString stringWithFormat:@"Unrecognized static class:%@ and selector:%@",NSStringFromClass(self.class),NSStringFromSelector(invocation.selector)];
handleCrashException(JJExceptionGuardUnrecognizedSelector,message);
}
@end
//
// NSObject+Zombie.h
// JJException
//
// Created by Jezz on 2018/7/26.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSObject (ZombieHook)
+ (void)jj_swizzleZombie;
@end
//
// NSObject+Zombie.m
// JJException
//
// Created by Jezz on 2018/7/26.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSObject+ZombieHook.h"
#import "NSObject+SwizzleHook.h"
#import <objc/runtime.h>
#import "JJExceptionProxy.h"
const NSInteger MAX_ARRAY_SIZE = 1024 * 1024 * 5;// MAX Memeory Size 5M
@interface ZombieSelectorHandle : NSObject
@property(nonatomic,readwrite,assign)id fromObject;
@end
@implementation ZombieSelectorHandle
void unrecognizedSelectorZombie(ZombieSelectorHandle* self, SEL _cmd){
}
@end
@interface JJZombieSub : NSObject
@end
@implementation JJZombieSub
- (id)forwardingTargetForSelector:(SEL)selector{
NSMethodSignature* sign = [self methodSignatureForSelector:selector];
if (!sign) {
id stub = [[ZombieSelectorHandle new] autorelease];
[stub setFromObject:self];
class_addMethod([stub class], selector, (IMP)unrecognizedSelectorZombie, "v@:");
return stub;
}
return [super forwardingTargetForSelector:selector];
}
@end
@implementation NSObject (ZombieHook)
+ (void)jj_swizzleZombie{
[self jj_swizzleInstanceMethod:@selector(dealloc) withSwizzleMethod:@selector(hookDealloc)];
}
- (void)hookDealloc{
Class currentClass = self.class;
//Check black list
if (![[[JJExceptionProxy shareExceptionProxy] blackClassesSet] containsObject:currentClass]) {
[self hookDealloc];
return;
}
//Check the array max size
//TODO:Real remove less than MAX_ARRAY_SIZE
if ([JJExceptionProxy shareExceptionProxy].currentClassSize > MAX_ARRAY_SIZE) {
id object = [[JJExceptionProxy shareExceptionProxy] objectFromCurrentClassesSet];
[[JJExceptionProxy shareExceptionProxy] removeCurrentZombieClass:object_getClass(object)];
object?free(object):nil;
}
objc_destructInstance(self);
object_setClass(self, [JJZombieSub class]);
[[JJExceptionProxy shareExceptionProxy] addCurrentZombieClass:currentClass];
}
@end
//
// JJException.h
// JJException
//
// Created by Jezz on 2018/7/21.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
Before start JJException,must config the JJExceptionGuardCategory
- JJExceptionGuardNone: Do not guard normal crash exception
- JJExceptionGuardUnrecognizedSelector: Unrecognized Selector Exception
- JJExceptionGuardDictionaryContainer: NSDictionary,NSMutableDictionary
- JJExceptionGuardArrayContainer: NSArray,NSMutableArray
- JJExceptionGuardZombie: Zombie
- JJExceptionGuardKVOCrash: KVO exception
- JJExceptionGuardNSTimer: NSTimer
- JJExceptionGuardNSNotificationCenter: NSNotificationCenter
- JJExceptionGuardNSStringContainer:NSString,NSMutableString,NSAttributedString,NSMutableAttributedString
- JJExceptionGuardAllExceptZombie:Above All except Zombie
- JJExceptionGuardAll: Above All
*/
typedef NS_OPTIONS(NSInteger,JJExceptionGuardCategory){
JJExceptionGuardNone = 0,
JJExceptionGuardUnrecognizedSelector = 1 << 1,
JJExceptionGuardDictionaryContainer = 1 << 2,
JJExceptionGuardArrayContainer = 1 << 3,
JJExceptionGuardZombie = 1 << 4,
JJExceptionGuardKVOCrash = 1 << 5,
JJExceptionGuardNSTimer = 1 << 6,
JJExceptionGuardNSNotificationCenter = 1 << 7,
JJExceptionGuardNSStringContainer = 1 << 8,
JJExceptionGuardAllExceptZombie = JJExceptionGuardUnrecognizedSelector | JJExceptionGuardDictionaryContainer | JJExceptionGuardArrayContainer | JJExceptionGuardKVOCrash | JJExceptionGuardNSTimer | JJExceptionGuardNSNotificationCenter | JJExceptionGuardNSStringContainer,
JJExceptionGuardAll = JJExceptionGuardUnrecognizedSelector | JJExceptionGuardDictionaryContainer | JJExceptionGuardArrayContainer | JJExceptionGuardZombie | JJExceptionGuardKVOCrash | JJExceptionGuardNSTimer | JJExceptionGuardNSNotificationCenter | JJExceptionGuardNSStringContainer,
};
/**
Exception interface
*/
@protocol JJExceptionHandle<NSObject>
/**
Crash message and extra info from current thread
@param exceptionMessage crash message
@param info extraInfo,key and value
*/
- (void)handleCrashException:(NSString*)exceptionMessage extraInfo:(nullable NSDictionary*)info;
@optional
/**
Crash message,exceptionCategory, extra info from current thread
@param exceptionMessage crash message
@param exceptionCategory JJExceptionGuardCategory
@param info extra info
*/
- (void)handleCrashException:(NSString*)exceptionMessage exceptionCategory:(JJExceptionGuardCategory)exceptionCategory extraInfo:(nullable NSDictionary*)info;
@end
/**
Exception main
*/
@interface JJException : NSObject
/**
If exceptionWhenTerminate YES,the exception will stop application
If exceptionWhenTerminate NO,the exception only show log on the console, will not stop the application
Default value:NO
*/
@property(class,nonatomic,readwrite,assign)BOOL exceptionWhenTerminate;
/**
JJException guard exception status,default is NO
*/
@property(class,nonatomic,readonly,assign)BOOL isGuardException;
/**
Config the guard exception category,default:JJExceptionGuardNone
@param exceptionGuardCategory JJExceptionGuardCategory
*/
+ (void)configExceptionCategory:(JJExceptionGuardCategory)exceptionGuardCategory;
/**
Start the exception protect
*/
+ (void)startGuardException;
/**
Stop the exception protect
* Why deprecated this method:
* https://github.com/jezzmemo/JJException/issues/54
*/
+ (void)stopGuardException __attribute__((deprecated("Stop invoke this method,If invoke this,Maybe occur the infinite loop and then CRASH")));
/**
Register exception interface
@param exceptionHandle JJExceptionHandle
*/
+ (void)registerExceptionHandle:(id<JJExceptionHandle>)exceptionHandle;
/**
Only handle the black list zombie object
Sample Code:
[JJException addZombieObjectArray:@[TestZombie.class]];
@param objects Class Array
*/
+ (void)addZombieObjectArray:(NSArray*)objects;
@end
NS_ASSUME_NONNULL_END
//
// JJException.m
// JJException
//
// Created by Jezz on 2018/7/21.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "JJException.h"
#import "JJExceptionProxy.h"
@implementation JJException
+ (BOOL)isGuardException {
return [JJExceptionProxy shareExceptionProxy].isProtectException;
}
+ (BOOL)exceptionWhenTerminate{
return [JJExceptionProxy shareExceptionProxy].exceptionWhenTerminate;
}
+ (void)setExceptionWhenTerminate:(BOOL)exceptionWhenTerminate{
[JJExceptionProxy shareExceptionProxy].exceptionWhenTerminate = exceptionWhenTerminate;
}
+ (void)startGuardException{
[JJExceptionProxy shareExceptionProxy].isProtectException = YES;
}
+ (void)stopGuardException{
[JJExceptionProxy shareExceptionProxy].isProtectException = NO;
}
+ (void)configExceptionCategory:(JJExceptionGuardCategory)exceptionGuardCategory{
[JJExceptionProxy shareExceptionProxy].exceptionGuardCategory = exceptionGuardCategory;
}
+ (void)registerExceptionHandle:(id<JJExceptionHandle>)exceptionHandle{
[JJExceptionProxy shareExceptionProxy].delegate = exceptionHandle;
}
+ (void)addZombieObjectArray:(NSArray*)objects{
[[JJExceptionProxy shareExceptionProxy] addZombieObjectArray:objects];
}
@end
//
// JJExceptionMacros.h
// JJException
//
// Created by Kealdish on 2019/2/26.
// Copyright © 2019 Jezz. All rights reserved.
//
#ifndef JJExceptionMacros_h
#define JJExceptionMacros_h
/**
Add this macro before each category implementation, so we don't have to use
-all_load or -force_load to load object files from static libraries that only
contain categories and no classes.
*******************************************************************************
Example:
JJSYNTH_DUMMY_CLASS(NSObject_DeallocBlock)
*/
#ifndef JJSYNTH_DUMMY_CLASS
#define JJSYNTH_DUMMY_CLASS(_name_) \
@interface JJSYNTH_DUMMY_CLASS_ ## _name_ : NSObject @end \
@implementation JJSYNTH_DUMMY_CLASS_ ## _name_ @end
#endif
#endif /* JJExceptionMacros_h */
//
// JJExceptionProxy.h
// JJException
//
// Created by Jezz on 2018/7/22.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "JJException.h"
NS_ASSUME_NONNULL_BEGIN
/**
C style invoke handle crash message
@param exceptionMessage crash message
*/
__attribute__((overloadable)) void handleCrashException(NSString* exceptionMessage);
/**
C style invoke handle crash message,and extra crash info
@param exceptionMessage crash message
@param extraInfo extra crash message
*/
__attribute__((overloadable)) void handleCrashException(NSString* exceptionMessage,NSDictionary* extraInfo);
/**
C style invoke handle crash message,and extra crash info
@param exceptionCategory crash type
@param exceptionMessage crash message
@param extraInfo extra info
*/
__attribute__((overloadable)) void handleCrashException(JJExceptionGuardCategory exceptionCategory, NSString* exceptionMessage,NSDictionary* extraInfo);
/**
C style invoke handle crash type,and exception message
@param exceptionCategory JJExceptionGuardCategory
@param exceptionMessage crash message
*/
__attribute__((overloadable)) void handleCrashException(JJExceptionGuardCategory exceptionCategory, NSString* exceptionMessage);
/**
Exception Proxy
*/
@interface JJExceptionProxy : NSObject<JJExceptionHandle>
+ (instancetype)shareExceptionProxy;
#pragma mark - Handle crash interface
/**
Hold the JJExceptionHandle interface object
*/
@property(nonatomic,readwrite,weak)id<JJExceptionHandle> delegate;
/**
Setting hook excpetion status,default value is NO
*/
@property(nonatomic,readwrite,assign)BOOL isProtectException;
/**
If exceptionWhenTerminate YES,the exception will stop application
If exceptionWhenTerminate NO,the exception only show log on the console, will not stop the application
Default value:NO
*/
@property(nonatomic,readwrite,assign)BOOL exceptionWhenTerminate;
/**
Setting exceptionGuardCategory
@see JJExceptionGuardCategory
*/
@property(nonatomic,readwrite,assign)JJExceptionGuardCategory exceptionGuardCategory;
#pragma mark - Zombie collection
/**
Real addZombieObjectArray invoke
@param objects class array
*/
- (void)addZombieObjectArray:(NSArray*)objects;
/**
Zombie only process the Set class
*/
@property(nonatomic,readonly,strong)NSSet* blackClassesSet;
/**
Record the all Set class size
*/
@property(nonatomic,readonly,assign)NSInteger currentClassSize;
/**
Add object to the currentClassesSet
@param object NSObject
*/
- (void)addCurrentZombieClass:(Class)object;
/**
Remove object from the currentClassesSet
@param object NSObject
*/
- (void)removeCurrentZombieClass:(Class)object;
/**
Record the objc_destructInstance instance object
*/
@property(nonatomic,readonly,strong)NSSet* currentClassesSet;
/**
Random get the object from blackClassesSet
@return NSObject
*/
- (nullable id)objectFromCurrentClassesSet;
@end
NS_ASSUME_NONNULL_END
//
// JJExceptionProxy.m
// JJException
//
// Created by Jezz on 2018/7/22.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "JJExceptionProxy.h"
#import <mach-o/dyld.h>
#import <objc/runtime.h>
__attribute__((overloadable)) void handleCrashException(NSString* exceptionMessage){
[[JJExceptionProxy shareExceptionProxy] handleCrashException:exceptionMessage extraInfo:@{}];
}
__attribute__((overloadable)) void handleCrashException(NSString* exceptionMessage,NSDictionary* extraInfo){
[[JJExceptionProxy shareExceptionProxy] handleCrashException:exceptionMessage extraInfo:extraInfo];
}
__attribute__((overloadable)) void handleCrashException(JJExceptionGuardCategory exceptionCategory, NSString* exceptionMessage,NSDictionary* extraInfo){
[[JJExceptionProxy shareExceptionProxy] handleCrashException:exceptionMessage exceptionCategory:exceptionCategory extraInfo:extraInfo];
}
__attribute__((overloadable)) void handleCrashException(JJExceptionGuardCategory exceptionCategory, NSString* exceptionMessage){
[[JJExceptionProxy shareExceptionProxy] handleCrashException:exceptionMessage exceptionCategory:exceptionCategory extraInfo:nil];
}
/**
Get application base address,the application different base address after started
@return base address
*/
uintptr_t get_load_address(void) {
const struct mach_header *exe_header = NULL;
for (uint32_t i = 0; i < _dyld_image_count(); i++) {
const struct mach_header *header = _dyld_get_image_header(i);
if (header->filetype == MH_EXECUTE) {
exe_header = header;
break;
}
}
return (uintptr_t)exe_header;
}
/**
Address Offset
@return slide address
*/
uintptr_t get_slide_address(void) {
uintptr_t vmaddr_slide = 0;
for (uint32_t i = 0; i < _dyld_image_count(); i++) {
const struct mach_header *header = _dyld_get_image_header(i);
if (header->filetype == MH_EXECUTE) {
vmaddr_slide = _dyld_get_image_vmaddr_slide(i);
break;
}
}
return (uintptr_t)vmaddr_slide;
}
@interface JJExceptionProxy(){
NSMutableSet* _currentClassesSet;
NSMutableSet* _blackClassesSet;
NSInteger _currentClassSize;
dispatch_semaphore_t _classArrayLock;//Protect _blackClassesSet and _currentClassesSet atomic
dispatch_semaphore_t _swizzleLock;//Protect swizzle atomic
}
@end
@implementation JJExceptionProxy
+(instancetype)shareExceptionProxy{
static dispatch_once_t onceToken;
static id exceptionProxy;
dispatch_once(&onceToken, ^{
exceptionProxy = [[self alloc] init];
});
return exceptionProxy;
}
- (instancetype)init{
self = [super init];
if (self) {
_blackClassesSet = [NSMutableSet new];
_currentClassesSet = [NSMutableSet new];
_currentClassSize = 0;
_classArrayLock = dispatch_semaphore_create(1);
_swizzleLock = dispatch_semaphore_create(1);
}
return self;
}
- (void)handleCrashException:(NSString *)exceptionMessage exceptionCategory:(JJExceptionGuardCategory)exceptionCategory extraInfo:(NSDictionary *)info{
if (!exceptionMessage) {
return;
}
NSArray* callStack = [NSThread callStackSymbols];
NSString* callStackString = [NSString stringWithFormat:@"%@",callStack];
uintptr_t loadAddress = get_load_address();
uintptr_t slideAddress = get_slide_address();
NSString* exceptionResult = [NSString stringWithFormat:@"%ld\n%ld\n%@\n%@",loadAddress,slideAddress,exceptionMessage,callStackString];
if ([self.delegate respondsToSelector:@selector(handleCrashException:extraInfo:)]){
[self.delegate handleCrashException:exceptionResult extraInfo:info];
}
if ([self.delegate respondsToSelector:@selector(handleCrashException:exceptionCategory:extraInfo:)]) {
[self.delegate handleCrashException:exceptionResult exceptionCategory:exceptionCategory extraInfo:info];
}
#ifdef DEBUG
NSLog(@"================================JJException Start==================================");
NSLog(@"JJException Type:%ld",(long)exceptionCategory);
NSLog(@"JJException Description:%@",exceptionMessage);
NSLog(@"JJException Extra info:%@",info);
NSLog(@"JJException CallStack:%@",callStack);
NSLog(@"================================JJException End====================================");
if (self.exceptionWhenTerminate) {
NSAssert(NO, @"");
}
#endif
}
- (void)handleCrashException:(NSString *)exceptionMessage extraInfo:(nullable NSDictionary *)info{
[self handleCrashException:exceptionMessage exceptionCategory:JJExceptionGuardNone extraInfo:info];
}
- (void)setIsProtectException:(BOOL)isProtectException{
dispatch_semaphore_wait(_swizzleLock, DISPATCH_TIME_FOREVER);
if (_isProtectException != isProtectException) {
_isProtectException = isProtectException;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
if(self.exceptionGuardCategory & JJExceptionGuardArrayContainer){
[NSArray performSelector:@selector(jj_swizzleNSArray)];
[NSMutableArray performSelector:@selector(jj_swizzleNSMutableArray)];
[NSSet performSelector:@selector(jj_swizzleNSSet)];
[NSMutableSet performSelector:@selector(jj_swizzleNSMutableSet)];
}
if(self.exceptionGuardCategory & JJExceptionGuardDictionaryContainer){
[NSDictionary performSelector:@selector(jj_swizzleNSDictionary)];
[NSMutableDictionary performSelector:@selector(jj_swizzleNSMutableDictionary)];
}
if(self.exceptionGuardCategory & JJExceptionGuardUnrecognizedSelector){
[NSObject performSelector:@selector(jj_swizzleUnrecognizedSelector)];
}
if (self.exceptionGuardCategory & JJExceptionGuardZombie) {
[NSObject performSelector:@selector(jj_swizzleZombie)];
}
if (self.exceptionGuardCategory & JJExceptionGuardKVOCrash) {
[NSObject performSelector:@selector(jj_swizzleKVOCrash)];
}
if (self.exceptionGuardCategory & JJExceptionGuardNSTimer) {
[NSTimer performSelector:@selector(jj_swizzleNSTimer)];
}
if (self.exceptionGuardCategory & JJExceptionGuardNSNotificationCenter) {
[NSNotificationCenter performSelector:@selector(jj_swizzleNSNotificationCenter)];
}
if (self.exceptionGuardCategory & JJExceptionGuardNSStringContainer) {
[NSString performSelector:@selector(jj_swizzleNSString)];
[NSMutableString performSelector:@selector(jj_swizzleNSMutableString)];
[NSAttributedString performSelector:@selector(jj_swizzleNSAttributedString)];
[NSMutableAttributedString performSelector:@selector(jj_swizzleNSMutableAttributedString)];
}
#pragma clang diagnostic pop
}
dispatch_semaphore_signal(_swizzleLock);
}
- (void)setExceptionGuardCategory:(JJExceptionGuardCategory)exceptionGuardCategory{
if (_exceptionGuardCategory != exceptionGuardCategory) {
_exceptionGuardCategory = exceptionGuardCategory;
}
}
- (void)addZombieObjectArray:(NSArray*)objects{
if (!objects) {
return;
}
dispatch_semaphore_wait(_classArrayLock, DISPATCH_TIME_FOREVER);
[_blackClassesSet addObjectsFromArray:objects];
dispatch_semaphore_signal(_classArrayLock);
}
- (NSSet*)blackClassesSet{
return _blackClassesSet;
}
- (void)addCurrentZombieClass:(Class)object{
if (object) {
dispatch_semaphore_wait(_classArrayLock, DISPATCH_TIME_FOREVER);
_currentClassSize = _currentClassSize + class_getInstanceSize(object);
[_currentClassesSet addObject:object];
dispatch_semaphore_signal(_classArrayLock);
}
}
- (void)removeCurrentZombieClass:(Class)object{
if (object) {
dispatch_semaphore_wait(_classArrayLock, DISPATCH_TIME_FOREVER);
_currentClassSize = _currentClassSize - class_getInstanceSize(object);
[_currentClassesSet removeObject:object];
dispatch_semaphore_signal(_classArrayLock);
}
}
- (NSSet*)currentClassesSet{
return _currentClassesSet;
}
- (NSInteger)currentClassSize{
return _currentClassSize;
}
- (nullable id)objectFromCurrentClassesSet{
NSEnumerator* objectEnum = [_currentClassesSet objectEnumerator];
for (id object in objectEnum) {
return object;
}
return nil;
}
@end
//
// NSObject+SwizzleHook.h
// JJException
//
// Created by Jezz on 2018/7/10.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import <Foundation/Foundation.h>
/*
* JJSwizzledIMPBlock assist variable
*/
typedef void (*JJSwizzleOriginalIMP)(void /* id, SEL, ... */ );
@interface JJSwizzleObject : NSObject
- (JJSwizzleOriginalIMP)getOriginalImplementation;
@property (nonatomic,readonly,assign) SEL selector;
@end
typedef id (^JJSwizzledIMPBlock)(JJSwizzleObject* swizzleInfo);
/*
* JJSwizzledIMPBlock assist variable
*/
/**
* Swizzle Class Method
@param cls Class
@param originSelector originSelector
@param swizzleSelector swizzleSelector
*/
void swizzleClassMethod(Class cls, SEL originSelector, SEL swizzleSelector);
/**
* Swizzle Instance Class Method
@param cls Class
@param originSelector originSelector
@param swizzleSelector swizzleSelector
*/
void swizzleInstanceMethod(Class cls, SEL originSelector, SEL swizzleSelector);
/**
* Only swizzle the current class,not swizzle all class
* perform jj_cleanKVO selector before the origin dealloc
@param class Class
*/
void jj_swizzleDeallocIfNeeded(Class class);
/**
Swizzle the NSObject Extension
*/
@interface NSObject (SwizzleHook)
/**
Swizzle Class Method
@param originSelector originSelector
@param swizzleSelector swizzleSelector
*/
+ (void)jj_swizzleClassMethod:(SEL)originSelector withSwizzleMethod:(SEL)swizzleSelector;
/**
Swizzle Instance Method
@param originSelector originSelector
@param swizzleSelector swizzleSelector
*/
- (void)jj_swizzleInstanceMethod:(SEL)originSelector withSwizzleMethod:(SEL)swizzleSelector;
/**
Swizzle instance method to the block target
@param originSelector originSelector
@param swizzledBlock block
*/
- (void)jj_swizzleInstanceMethod:(SEL)originSelector withSwizzledBlock:(JJSwizzledIMPBlock)swizzledBlock;
@end
//
// NSObject+SwizzleHook.m
// JJException
//
// Created by Jezz on 2018/7/10.
// Copyright © 2018年 Jezz. All rights reserved.
//
#import "NSObject+SwizzleHook.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import <libkern/OSAtomic.h>
typedef IMP (^JJSWizzleImpProvider)(void);
static const char jjSwizzledDeallocKey;
@interface JJSwizzleObject()
@property (nonatomic,readwrite,copy) JJSWizzleImpProvider impProviderBlock;
@property (nonatomic,readwrite,assign) SEL selector;
@end
@implementation JJSwizzleObject
- (JJSwizzleOriginalIMP)getOriginalImplementation{
NSAssert(_impProviderBlock,nil);
return (JJSwizzleOriginalIMP)_impProviderBlock();
}
@end
void swizzleClassMethod(Class cls, SEL originSelector, SEL swizzleSelector){
if (!cls) {
return;
}
Method originalMethod = class_getClassMethod(cls, originSelector);
Method swizzledMethod = class_getClassMethod(cls, swizzleSelector);
Class metacls = objc_getMetaClass(NSStringFromClass(cls).UTF8String);
if (class_addMethod(metacls,
originSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod)) ) {
/* swizzing super class method, added if not exist */
class_replaceMethod(metacls,
swizzleSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
/* swizzleMethod maybe belong to super */
class_replaceMethod(metacls,
swizzleSelector,
class_replaceMethod(metacls,
originSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod)),
method_getTypeEncoding(originalMethod));
}
}
void swizzleInstanceMethod(Class cls, SEL originSelector, SEL swizzleSelector){
if (!cls) {
return;
}
/* if current class not exist selector, then get super*/
Method originalMethod = class_getInstanceMethod(cls, originSelector);
Method swizzledMethod = class_getInstanceMethod(cls, swizzleSelector);
/* add selector if not exist, implement append with method */
if (class_addMethod(cls,
originSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod)) ) {
/* replace class instance method, added if selector not exist */
/* for class cluster , it always add new selector here */
class_replaceMethod(cls,
swizzleSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
/* swizzleMethod maybe belong to super */
class_replaceMethod(cls,
swizzleSelector,
class_replaceMethod(cls,
originSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod)),
method_getTypeEncoding(originalMethod));
}
}
// a class doesn't need dealloc swizzled if it or a superclass has been swizzled already
BOOL jj_requiresDeallocSwizzle(Class class)
{
BOOL swizzled = NO;
for ( Class currentClass = class; !swizzled && currentClass != nil; currentClass = class_getSuperclass(currentClass) ) {
swizzled = [objc_getAssociatedObject(currentClass, &jjSwizzledDeallocKey) boolValue];
}
return !swizzled;
}
void jj_swizzleDeallocIfNeeded(Class class)
{
static SEL deallocSEL = NULL;
static SEL cleanupSEL = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
deallocSEL = sel_getUid("dealloc");
cleanupSEL = sel_getUid("jj_cleanKVO");
});
@synchronized (class) {
if ( !jj_requiresDeallocSwizzle(class) ) {
return;
}
objc_setAssociatedObject(class, &jjSwizzledDeallocKey, @(YES), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
Method dealloc = class_getInstanceMethod(class, deallocSEL);
if ( dealloc == NULL ) {
Class superclass = class_getSuperclass(class);
class_addMethod(class, deallocSEL, imp_implementationWithBlock(^(__unsafe_unretained id self) {
((void(*)(id, SEL))objc_msgSend)(self, cleanupSEL);
struct objc_super superStruct = (struct objc_super){ self, superclass };
((void (*)(struct objc_super*, SEL))objc_msgSendSuper)(&superStruct, deallocSEL);
}), method_getTypeEncoding(dealloc));
}else{
__block IMP deallocIMP = method_setImplementation(dealloc, imp_implementationWithBlock(^(__unsafe_unretained id self) {
((void(*)(id, SEL))objc_msgSend)(self, cleanupSEL);
((void(*)(id, SEL))deallocIMP)(self, deallocSEL);
}));
}
}
@implementation NSObject (SwizzleHook)
void __JJ_SWIZZLE_BLOCK(Class classToSwizzle,SEL selector,JJSwizzledIMPBlock impBlock){
Method method = class_getInstanceMethod(classToSwizzle, selector);
__block IMP originalIMP = NULL;
JJSWizzleImpProvider originalImpProvider = ^IMP{
IMP imp = originalIMP;
if (NULL == imp){
Class superclass = class_getSuperclass(classToSwizzle);
imp = method_getImplementation(class_getInstanceMethod(superclass,selector));
}
return imp;
};
JJSwizzleObject* swizzleInfo = [JJSwizzleObject new];
swizzleInfo.selector = selector;
swizzleInfo.impProviderBlock = originalImpProvider;
id newIMPBlock = impBlock(swizzleInfo);
const char* methodType = method_getTypeEncoding(method);
IMP newIMP = imp_implementationWithBlock(newIMPBlock);
originalIMP = class_replaceMethod(classToSwizzle, selector, newIMP, methodType);
}
+ (void)jj_swizzleClassMethod:(SEL)originSelector withSwizzleMethod:(SEL)swizzleSelector{
swizzleClassMethod(self.class, originSelector, swizzleSelector);
}
- (void)jj_swizzleInstanceMethod:(SEL)originSelector withSwizzleMethod:(SEL)swizzleSelector{
swizzleInstanceMethod(self.class, originSelector, swizzleSelector);
}
- (void)jj_swizzleInstanceMethod:(SEL)originSelector withSwizzledBlock:(JJSwizzledIMPBlock)swizzledBlock{
__JJ_SWIZZLE_BLOCK(self.class, originSelector, swizzledBlock);
}
@end
MIT License
Copyright (c) 2018 jezz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/JJException.svg)](https://img.shields.io/cocoapods/v/JJException.svg)
[![Build Status](https://travis-ci.org/jezzmemo/JJException.svg?branch=master)](https://travis-ci.org/jezzmemo/JJException.svg?branch=master)
[![codecov](https://codecov.io/gh/jezzmemo/JJException/branch/master/graph/badge.svg)](https://codecov.io/gh/jezzmemo/JJException)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Platform](https://img.shields.io/cocoapods/p/JJException.svg?style=flat)](http://cocoadocs.org/docsets/JJException)
![License MIT](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000)
# JJException
Common problems will not crash by the JJException,Hook the Unrecognized Selector,Out of bound,Paramter is nil,etc.Throw the exception to the interface,And then save the exception record to the log,Upgrad the app or Hot-Fix to resolve the exception.
保护App,一般常见的问题不会导致闪退,增强App的健壮性,同时会将错误抛出来,根据每个App自身的日志渠道记录,下次迭代或者热修复以下问题.
- [x] Unrecognized Selector Sent to Instance(方法不存在异常)
- [x] NSNull(方法不存在异常)
- [x] NSArray,NSMutableArray,NSDictonary,NSMutableDictionary(数组越界,key-value参数异常)
- [x] KVO(忘记移除keypath导致闪退)
- [x] Zombie Pointer(野指针)
- [x] NSTimer(忘记移除导致内存泄漏)
- [x] NSNotification(忘记移除导致异常)
- [x] NSString,NSMutableString,NSAttributedString,NSMutableAttributedString(下标越界以及参数nil异常)
## 如何安装
__Requirements__
* iOS 8.0+
* OSX 10.7+
* Xcode 8.0+
__Podfile__
```
pod 'JJException'
```
__Cartfile__
```
github "jezzmemo/JJException"
```
__手动导入代码__
导入`Source`文件夹里所有文件,需要将`MRC`目录下所有.m文件,编译选项更改成-fno-objc-arc
## 如何使用
* 所有异常的分类,根据自身需要,自由组合,__如果没用到Zombie功能,建议使用JJExceptionGuardAllExceptZombie__
```objc
typedef NS_OPTIONS(NSInteger,JJExceptionGuardCategory){
JJExceptionGuardNone = 0,
JJExceptionGuardUnrecognizedSelector = 1 << 1,
JJExceptionGuardDictionaryContainer = 1 << 2,
JJExceptionGuardArrayContainer = 1 << 3,
JJExceptionGuardZombie = 1 << 4,
JJExceptionGuardKVOCrash = 1 << 5,
JJExceptionGuardNSTimer = 1 << 6,
JJExceptionGuardNSNotificationCenter = 1 << 7,
JJExceptionGuardNSStringContainer = 1 << 8,
JJExceptionGuardAllExceptZombie = JJExceptionGuardUnrecognizedSelector | JJExceptionGuardDictionaryContainer | JJExceptionGuardArrayContainer | JJExceptionGuardKVOCrash | JJExceptionGuardNSTimer | JJExceptionGuardNSNotificationCenter | JJExceptionGuardNSStringContainer,
JJExceptionGuardAll = JJExceptionGuardUnrecognizedSelector | JJExceptionGuardDictionaryContainer | JJExceptionGuardArrayContainer | JJExceptionGuardZombie | JJExceptionGuardKVOCrash | JJExceptionGuardNSTimer | JJExceptionGuardNSNotificationCenter | JJExceptionGuardNSStringContainer,
};
```
* 设置异常类型并开启,__建议放在`didFinishLaunchingWithOptions`第一行,以免在多线程出现异常的情况__
```objc
[JJException configExceptionCategory:JJExceptionGuardAll];
[JJException startGuardException];
```
* 实时关闭保护
```objc
[JJException stopGuardException];
```
* 当异常时,默认程序不会中断,如果需要遇到异常时退出,需要如下设置:
```objc
//Default value:NO
JJException.exceptionWhenTerminate = YES;
```
* Zombie使用黑名单机制,只有加入这个名单的才有作用,示例如下:
```objc
[JJException addZombieObjectArray:@[TestZombie.class]];
```
* 如果需要记录日志,只需要实现`JJExceptionHandle`协议,并注册:
```objc
@interface ViewController ()<JJExceptionHandle>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[JJException registerExceptionHandle:self];
}
- (void)handleCrashException:(NSString*)exceptionMessage exceptionCategory:(JJExceptionGuardCategory)exceptionCategory extraInfo:(nullable NSDictionary*)info{
}
```
## FAQ
> 是否影响上线App Store
不会的,JJException的功能都是使用的官方API,没有任何私有API
> 保护App的实现技术原理是什么?
[JJException技术原理](https://github.com/jezzmemo/JJException/blob/master/JJExceptionPrinciple.md)
> JJException是否和Bugly和友盟等第三方库是否有冲突?
Bugly和友盟是记录Crash Bug的log还有一些统计功能,JJException主要是通过Hook技术来实现,所以不会和JJException冲突
> 如何上传异常信息到Bugly?
Bugly可以帮我们解决重复信息和CallStack信息,以及状态维护。
实现JJExceptionHandle协议后,将异常信息组织成Error,然后用[Bugly reportError:error]上传异常信息,上传后异常信息Bugly的后台`错误分析`菜单里
> Swift是否有作用
是有作用的,Swift有些API实现是独立实现的,比如String,Array,用结构体的方式,但是有些还是沿用了Objective-c,凡是沿用Objective-c的特性的,JJException还是生效的,下面我来列下还依然生效的功能点:
* Unrecognized Selector Sent to Instance
* NSNull
* KVO
* NSNotification
* NSString,NSMutableString,NSAttributedString,NSMutableAttributedString(__注意不是String__)
* NSArray,NSMutableArray,NSDictonary,NSMutableDictionary(__注意不是Array__)
* Zombie Pointer
这里贴下Swift的初始化代码示例:
```swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.registerJJException()
return true
}
func registerJJException(){
JJException.configExceptionCategory(.allExceptZombie)
JJException.startGuard()
JJException.register(self);
}
func handleCrashException(_ exceptionMessage: String, extraInfo info: [AnyHashable : Any]?) {
}
```
> JJException Hook那些API?
[HookAPI](https://github.com/jezzmemo/JJException/blob/master/JJExceptionHookAPI.md)
## TODO(大家记得给我星哦)
* 国际化JJException
## License
JJException is released under the MIT license. See LICENSE for details.
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0930"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForAnalyzing = "YES"
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9C67FC40C7E15E74BE99DE8F3435F9D9"
BuildableName = "libJJException.a"
BlueprintName = "JJException"
ReferencedContainer = "container:Pods.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
buildConfiguration = "Debug"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
#import <Foundation/Foundation.h>
@interface PodsDummy_JJException : NSObject
@end
@implementation PodsDummy_JJException
@end
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/JJException
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/JJException" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/JJException"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/JJException
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment