Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

添加了对KVC崩溃的Hook #53

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions JJException.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
61A7F5602144ED9D00C87FA1 /* NSNotificationCenter+ClearNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A7F55D2144ED9C00C87FA1 /* NSNotificationCenter+ClearNotification.m */; };
61A7F5632145071800C87FA1 /* KVOObjectDemo.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A7F5622145071800C87FA1 /* KVOObjectDemo.m */; };
61BC3FC720F7ACE500D8C791 /* JJExceptionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 61BC3FC620F7ACE500D8C791 /* JJExceptionTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
B6500EC022018563006CE751 /* NSObject+KVCCrash.m in Sources */ = {isa = PBXBuildFile; fileRef = B6500EBF22018563006CE751 /* NSObject+KVCCrash.m */; };
B6500EC3220186CF006CE751 /* JJPerson.m in Sources */ = {isa = PBXBuildFile; fileRef = B6500EC2220186CF006CE751 /* JJPerson.m */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -144,6 +146,10 @@
61BC3FC420F7ACE500D8C791 /* JJExceptionTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JJExceptionTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
61BC3FC620F7ACE500D8C791 /* JJExceptionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JJExceptionTests.m; sourceTree = "<group>"; };
61BC3FC820F7ACE500D8C791 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B6500EBE22018563006CE751 /* NSObject+KVCCrash.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSObject+KVCCrash.h"; sourceTree = "<group>"; };
B6500EBF22018563006CE751 /* NSObject+KVCCrash.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSObject+KVCCrash.m"; sourceTree = "<group>"; };
B6500EC1220186CF006CE751 /* JJPerson.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JJPerson.h; sourceTree = "<group>"; };
B6500EC2220186CF006CE751 /* JJPerson.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JJPerson.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -216,6 +222,8 @@
61A7F51B213B859300C87FA1 /* PushViewController.m */,
61A7F5612145071800C87FA1 /* KVOObjectDemo.h */,
61A7F5622145071800C87FA1 /* KVOObjectDemo.m */,
B6500EC1220186CF006CE751 /* JJPerson.h */,
B6500EC2220186CF006CE751 /* JJPerson.m */,
);
path = JJException;
sourceTree = "<group>";
Expand Down Expand Up @@ -301,6 +309,8 @@
6109EE792198244A00CB15E6 /* NSSet+SetHook.m */,
6109EE7B2198264F00CB15E6 /* NSMutableSet+MutableSetHook.h */,
6109EE7C2198264F00CB15E6 /* NSMutableSet+MutableSetHook.m */,
B6500EBE22018563006CE751 /* NSObject+KVCCrash.h */,
B6500EBF22018563006CE751 /* NSObject+KVCCrash.m */,
);
path = ARC;
sourceTree = "<group>";
Expand Down Expand Up @@ -504,6 +514,8 @@
61025B4D2153E36100191522 /* NSAttributedString+AttributedStringHook.m in Sources */,
61A7F54321427E7F00C87FA1 /* NSObject+KVOCrash.m in Sources */,
61A7F54F21427EB700C87FA1 /* NSMutableDictionary+MutableDictionaryHook.m in Sources */,
B6500EC022018563006CE751 /* NSObject+KVCCrash.m in Sources */,
B6500EC3220186CF006CE751 /* JJPerson.m in Sources */,
61196D4A20F261C800022DDC /* ViewController.m in Sources */,
613E0ABA21514E2E00B8D0DF /* NSMutableString+MutableStringHook.m in Sources */,
613E0A95214974B800B8D0DF /* JJExceptionProxy.m in Sources */,
Expand Down
24 changes: 24 additions & 0 deletions JJException/JJPerson.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// JJPerson.h
// JJException
//
// Created by 无头骑士 GJ on 2019/1/30.
// Copyright © 2019 Jezz. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface JJPerson : NSObject
{
int age;
}


@property (nonatomic, strong) NSString *name;


@end

NS_ASSUME_NONNULL_END
16 changes: 16 additions & 0 deletions JJException/JJPerson.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// JJPerson.m
// JJException
//
// Created by 无头骑士 GJ on 2019/1/30.
// Copyright © 2019 Jezz. All rights reserved.
//

#import "JJPerson.h"

@implementation JJPerson
+ (BOOL)accessInstanceVariablesDirectly
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

函数上下各空一行,大括号紧跟在函数名且空一个空格

{
return YES;
}
@end
17 changes: 17 additions & 0 deletions JJException/Source/ARC/NSObject+KVCCrash.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// NSObject+KVCCrash.h
// JJException
//
// Created by 无头骑士 GJ on 2019/1/30.
// Copyright © 2019 Jezz. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (KVCCrash)
+ (void)jj_swizzleKVCCrash;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

函数上下各空一行

@end

NS_ASSUME_NONNULL_END
100 changes: 100 additions & 0 deletions JJException/Source/ARC/NSObject+KVCCrash.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// NSObject+KVCCrash.m
// JJException
//
// Created by 无头骑士 GJ on 2019/1/30.
// Copyright © 2019 Jezz. All rights reserved.
//

#import "NSObject+KVCCrash.h"
#import "NSObject+SwizzleHook.h"
#import "JJExceptionProxy.h"
#import <objc/message.h>

@implementation NSObject (KVCCrash)
+ (void)jj_swizzleKVCCrash
{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

大括号问题

swizzleInstanceMethod([self class], @selector(setValue:forKey:), @selector(hookSetValue:forKey:));
}

- (void)hookSetValue:(id)value forKey:(NSString *)key
{
if (key.length == 0) return;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (key.length == 0) {
return;
}



NSString *methodSuffix = [NSString stringWithFormat: @"%@%@", [[key substringToIndex: 1] uppercaseString], [key substringFromIndex: 1]];

// 1、判断setKey方法是否存在
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

注释最好用英文

SEL setXXSelector = NSSelectorFromString([NSString stringWithFormat: @"set%@", methodSuffix]);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

建议不要用XX这种命名,明确变量的含义

Method setXXMethod = class_getInstanceMethod([self class], setXXSelector);

if (setXXMethod)
{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

大括号问题

[self hookSetValue: value forKey: key];
return;
}

// 2、判断_setKey方法是否存在
SEL _setXXSelector = NSSelectorFromString([NSString stringWithFormat: @"_set%@", methodSuffix]);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不要XX这种命名,明确变量的意思

Method _setXXMethod = class_getInstanceMethod([self class], _setXXSelector);
if (_setXXMethod)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

大括号问题

{
[self hookSetValue: value forKey: key];
return;
}

// 3、accessInstanceVariablesDirectly的返回值,如果为NO,直接抛出异常
SEL accessInstanceVariablesDirectlySEL = @selector(accessInstanceVariablesDirectly);
NSMethodSignature *methodSignature = [NSMethodSignature methodSignatureForSelector: accessInstanceVariablesDirectlySEL];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature: methodSignature];
[invocation setSelector: accessInstanceVariablesDirectlySEL];
[invocation invokeWithTarget: [self class]];

BOOL result;
[invocation getReturnValue: &result];

// 异常处理
if (result == NO)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

布尔类型不需要==,直接if (result)这种就行,还有括号问题

{
handleCrashException(JJExceptionGuardKVCCrash, @"hookSetValue:forKey: key is not exist");
return;
}

// 4、检查是否存在 _key、_isKey、key, isKey任一属性是否包含在成员变量中
NSMutableArray *keys = [NSMutableArray array];
[keys addObject: [NSString stringWithFormat: @"_%@", key]];
[keys addObject: [NSString stringWithFormat: @"_is%@", methodSuffix]];
[keys addObject: [NSString stringWithFormat: @"%@", key]];
[keys addObject: [NSString stringWithFormat: @"is%@", methodSuffix]];

unsigned int count = 0;
Ivar *members = class_copyIvarList([self class], &count);

__block BOOL find = NO;
for (int i = 0; i < count; i++)
{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

第一个括号不需要换行

Ivar var = members[i];
NSString *memeberName = [[NSString alloc] initWithUTF8String: ivar_getName(var)];

if (![[memeberName lowercaseString] containsString: key]) continue;

[keys enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

if ([obj isEqualToString: memeberName])
{
find = YES;
[self hookSetValue: value forKey: key];
*stop = YES;
}
}];

if (find) break;

}

if (find == NO)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

布尔类型和括号问题

handleCrashException(JJExceptionGuardKVCCrash, @"hookSetValue:forKey: key is not exist");
}


@end
3 changes: 2 additions & 1 deletion JJException/Source/Main/JJException.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ typedef NS_OPTIONS(NSInteger,JJExceptionGuardCategory){
JJExceptionGuardNSTimer = 1 << 6,
JJExceptionGuardNSNotificationCenter = 1 << 7,
JJExceptionGuardNSStringContainer = 1 << 8,
JJExceptionGuardKVCCrash = 1 << 9,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

麻烦写下这个枚举的注释,用英文

JJExceptionGuardAllExceptZombie = JJExceptionGuardUnrecognizedSelector | JJExceptionGuardDictionaryContainer | JJExceptionGuardArrayContainer | JJExceptionGuardKVOCrash | JJExceptionGuardNSTimer | JJExceptionGuardNSNotificationCenter | JJExceptionGuardNSStringContainer,
JJExceptionGuardAll = JJExceptionGuardUnrecognizedSelector | JJExceptionGuardDictionaryContainer | JJExceptionGuardArrayContainer | JJExceptionGuardZombie | JJExceptionGuardKVOCrash | JJExceptionGuardNSTimer | JJExceptionGuardNSNotificationCenter | JJExceptionGuardNSStringContainer,
JJExceptionGuardAll = JJExceptionGuardUnrecognizedSelector | JJExceptionGuardDictionaryContainer | JJExceptionGuardArrayContainer | JJExceptionGuardZombie | JJExceptionGuardKVOCrash | JJExceptionGuardNSTimer | JJExceptionGuardNSNotificationCenter | JJExceptionGuardNSStringContainer | JJExceptionGuardKVCCrash,
};

/**
Expand Down
5 changes: 5 additions & 0 deletions JJException/Source/Main/JJExceptionProxy.m
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,11 @@ - (void)setIsProtectException:(BOOL)isProtectException{
[NSAttributedString performSelector:@selector(jj_swizzleNSAttributedString)];
[NSMutableAttributedString performSelector:@selector(jj_swizzleNSMutableAttributedString)];
}

if (self.exceptionGuardCategory & JJExceptionGuardKVCCrash)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

括号不需要换行

{
[NSObject performSelector: @selector(jj_swizzleKVCCrash)];
}
#pragma clang diagnostic pop
}
dispatch_semaphore_signal(_swizzleLock);
Expand Down
16 changes: 16 additions & 0 deletions JJException/ViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#import "ViewController.h"
#import "JJException.h"
#import <objc/runtime.h>
#import "JJPerson.h"
#import "PushViewController.h"

@interface ViewController ()<JJExceptionHandle>
Expand Down Expand Up @@ -48,6 +49,13 @@ - (void)viewDidLoad {
otherButton.frame = CGRectMake(0, 350, self.view.frame.size.width, 50);
[otherButton addTarget:self action:@selector(testArrayDictionaryUnrecognizedSelector) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:otherButton];

UIButton* kvcButton = [UIButton buttonWithType:UIButtonTypeSystem];
[kvcButton setTitle:@"Test KVC" forState:UIControlStateNormal];
[kvcButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
kvcButton.frame = CGRectMake(0, 450, self.view.frame.size.width, 50);
[kvcButton addTarget:self action:@selector(keyButtonClick) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:kvcButton];
}
#pragma mark - Exception Delegate

Expand Down Expand Up @@ -106,4 +114,12 @@ - (void)testNull{
NSLog(@"Str length:%ld",str.length);
}

- (void)keyButtonClick
{
JJPerson *p = [JJPerson new];
[p setValue: @"123" forKeyPath: @"age"];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

没开启保护时,也不会闪退,你应该测试异常和正常情况


NSLog(@"%@", [p valueForKey: @"age"]);
}

@end