From bb1e87e0d80c1cb14312924fb366b1c4d9f786a4 Mon Sep 17 00:00:00 2001 From: "Justin.wang" Date: Mon, 19 Mar 2018 13:08:41 +0800 Subject: [PATCH] add message prefix to avoid crash --- Aspects.h | 8 +++ Aspects.m | 29 ++++++++ .../AspectsDemo.xcodeproj/project.pbxproj | 14 ++++ AspectsDemo/AspectsDemo/AspectsSwizz.h | 22 ++++++ AspectsDemo/AspectsDemo/AspectsSwizz.m | 71 +++++++++++++++++++ .../AspectsDemoTests/AspectsHookTests.m | 60 ++++++++++++++++ 6 files changed, 204 insertions(+) create mode 100644 AspectsDemo/AspectsDemo/AspectsSwizz.h create mode 100644 AspectsDemo/AspectsDemo/AspectsSwizz.m create mode 100644 AspectsDemo/AspectsDemoTests/AspectsHookTests.m diff --git a/Aspects.h b/Aspects.h index 5508f86..f97f61d 100644 --- a/Aspects.h +++ b/Aspects.h @@ -7,6 +7,14 @@ #import +@interface AspectManager : NSObject + +@property (nonatomic, copy) NSArray *messagePrefixArray; + ++ (instancetype)shareManager; + +@end + typedef NS_OPTIONS(NSUInteger, AspectOptions) { AspectPositionAfter = 0, /// Called after the original implementation (default) AspectPositionInstead = 1, /// Will replace the original implementation. diff --git a/Aspects.m b/Aspects.m index c907066..77d1893 100644 --- a/Aspects.m +++ b/Aspects.m @@ -10,6 +10,19 @@ #import #import +@implementation AspectManager + ++ (instancetype)shareManager { + static AspectManager *shareManager; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + shareManager = [[AspectManager alloc] init]; + }); + return shareManager; +} + +@end + #define AspectLog(...) //#define AspectLog(...) do { NSLog(__VA_ARGS__); }while(0) #define AspectLogError(...) do { NSLog(__VA_ARGS__); }while(0) @@ -474,6 +487,22 @@ static void aspect_undoSwizzleClassInPlace(Class klass) { static void __ASPECTS_ARE_BEING_CALLED__(__unsafe_unretained NSObject *self, SEL selector, NSInvocation *invocation) { NSCParameterAssert(self); NSCParameterAssert(invocation); + /* + The aspects framework hook firstly and later other aop framework hook a same method, which will cause crash. + */ + NSString *selectorStr = NSStringFromSelector(invocation.selector); + if ([AspectManager shareManager].messagePrefixArray.count > 0) { + [[AspectManager shareManager].messagePrefixArray enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if ([selectorStr hasPrefix:obj]) { + NSString *cstr = + [selectorStr stringByReplacingCharactersInRange:NSMakeRange(0, obj.length) + withString:@""]; + invocation.selector = NSSelectorFromString(cstr); + *stop = YES; + } + }]; + } + SEL originalSelector = invocation.selector; SEL aliasSelector = aspect_aliasForSelector(invocation.selector); invocation.selector = aliasSelector; diff --git a/AspectsDemo/AspectsDemo.xcodeproj/project.pbxproj b/AspectsDemo/AspectsDemo.xcodeproj/project.pbxproj index ecec2b8..d7d10a3 100644 --- a/AspectsDemo/AspectsDemo.xcodeproj/project.pbxproj +++ b/AspectsDemo/AspectsDemo.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ 78573F2519155A74000D3B00 /* Aspects.m in Sources */ = {isa = PBXBuildFile; fileRef = 78573F2319155A74000D3B00 /* Aspects.m */; }; 78D7D77119177C8E002EB314 /* AspectsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 78D7D76F19177C8E002EB314 /* AspectsViewController.m */; }; 78D7D77219177C8E002EB314 /* AspectsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 78D7D77019177C8E002EB314 /* AspectsViewController.xib */; }; + BA8B9D77205F635C00D6AA5B /* AspectsSwizz.m in Sources */ = {isa = PBXBuildFile; fileRef = BA8B9D76205F635C00D6AA5B /* AspectsSwizz.m */; }; + BA8B9D79205F645D00D6AA5B /* AspectsHookTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BA8B9D78205F645D00D6AA5B /* AspectsHookTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -56,6 +58,9 @@ 78D7D76E19177C8E002EB314 /* AspectsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AspectsViewController.h; sourceTree = ""; }; 78D7D76F19177C8E002EB314 /* AspectsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AspectsViewController.m; sourceTree = ""; }; 78D7D77019177C8E002EB314 /* AspectsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AspectsViewController.xib; sourceTree = ""; }; + BA8B9D75205F635C00D6AA5B /* AspectsSwizz.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AspectsSwizz.h; sourceTree = ""; }; + BA8B9D76205F635C00D6AA5B /* AspectsSwizz.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AspectsSwizz.m; sourceTree = ""; }; + BA8B9D78205F645D00D6AA5B /* AspectsHookTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AspectsHookTests.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -124,6 +129,8 @@ 78D7D76E19177C8E002EB314 /* AspectsViewController.h */, 78D7D76F19177C8E002EB314 /* AspectsViewController.m */, 78D7D77019177C8E002EB314 /* AspectsViewController.xib */, + BA8B9D75205F635C00D6AA5B /* AspectsSwizz.h */, + BA8B9D76205F635C00D6AA5B /* AspectsSwizz.m */, ); path = AspectsDemo; sourceTree = ""; @@ -143,6 +150,7 @@ isa = PBXGroup; children = ( 78573F1919155A2E000D3B00 /* AspectsDemoTests.m */, + BA8B9D78205F645D00D6AA5B /* AspectsHookTests.m */, 78573F1419155A2E000D3B00 /* Supporting Files */, ); path = AspectsDemoTests; @@ -256,6 +264,7 @@ files = ( 78D7D77119177C8E002EB314 /* AspectsViewController.m in Sources */, 78573F0519155A2E000D3B00 /* AspectsAppDelegate.m in Sources */, + BA8B9D77205F635C00D6AA5B /* AspectsSwizz.m in Sources */, 78573F2519155A74000D3B00 /* Aspects.m in Sources */, 78573F0119155A2E000D3B00 /* main.m in Sources */, ); @@ -265,6 +274,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + BA8B9D79205F645D00D6AA5B /* AspectsHookTests.m in Sources */, 78573F1A19155A2E000D3B00 /* AspectsDemoTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -413,6 +423,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "AspectsDemo/AspectsDemo-Prefix.pch"; INFOPLIST_FILE = "AspectsDemo/AspectsDemo-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; @@ -426,6 +437,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "AspectsDemo/AspectsDemo-Prefix.pch"; INFOPLIST_FILE = "AspectsDemo/AspectsDemo-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; @@ -447,6 +459,7 @@ "$(inherited)", ); INFOPLIST_FILE = "AspectsDemoTests/AspectsDemoTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = xctest; @@ -465,6 +478,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "AspectsDemo/AspectsDemo-Prefix.pch"; INFOPLIST_FILE = "AspectsDemoTests/AspectsDemoTests-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = xctest; diff --git a/AspectsDemo/AspectsDemo/AspectsSwizz.h b/AspectsDemo/AspectsDemo/AspectsSwizz.h new file mode 100644 index 0000000..68a3a9e --- /dev/null +++ b/AspectsDemo/AspectsDemo/AspectsSwizz.h @@ -0,0 +1,22 @@ +// +// AspectsSwizz.h +// AspectsDemo +// +// Created by Justin.wang on 2018/3/19. +// Copyright © 2018年 PSPDFKit GmbH. All rights reserved. +// + +#import +#import + +@interface AspectsSwizz : NSObject + ++ (void)swizzingInstanceMethodByClass:(Class)cls + originSelector:(SEL)originSelector + swizzingSelector:(SEL)swizzingSelector; + ++ (void)swizzingClassMethodByClass:(Class)cls + originSelector:(SEL)originSelector + swizzingSelector:(SEL)swizzingSelector; + +@end diff --git a/AspectsDemo/AspectsDemo/AspectsSwizz.m b/AspectsDemo/AspectsDemo/AspectsSwizz.m new file mode 100644 index 0000000..05d0c13 --- /dev/null +++ b/AspectsDemo/AspectsDemo/AspectsSwizz.m @@ -0,0 +1,71 @@ +// +// AspectsSwizz.m +// AspectsDemo +// +// Created by Justin.wang on 2018/3/19. +// Copyright © 2018年 PSPDFKit GmbH. All rights reserved. +// + +#import "AspectsSwizz.h" + +@implementation AspectsSwizz + ++ (void)swizzingInstanceMethodByClass:(Class)cls + originSelector:(SEL)originSelector + swizzingSelector:(SEL)swizzingSelector { + NSParameterAssert(cls); + NSParameterAssert(originSelector); + NSParameterAssert(swizzingSelector); + + Method originalMethod = class_getInstanceMethod(cls, originSelector); + Method swizzledMethod = class_getInstanceMethod(cls, swizzingSelector); + + if (originalMethod && swizzledMethod) { + BOOL didAddMethod = + class_addMethod(cls, + originSelector, + method_getImplementation(swizzledMethod), + method_getTypeEncoding(swizzledMethod)); + + if (didAddMethod) { + class_replaceMethod(cls, + swizzingSelector, + method_getImplementation(originalMethod), + method_getTypeEncoding(originalMethod)); + } else { + method_exchangeImplementations(originalMethod, swizzledMethod); + } + } +} + ++ (void)swizzingClassMethodByClass:(Class)cls + originSelector:(SEL)originSelector + swizzingSelector:(SEL)swizzingSelector { + NSParameterAssert(cls); + NSParameterAssert(originSelector); + NSParameterAssert(swizzingSelector); + + Method originalMethod = class_getClassMethod(cls, originSelector); + Method swizzledMethod = class_getClassMethod(cls, swizzingSelector); + + cls = object_getClass((id)cls); + + if (originalMethod && swizzledMethod) { + BOOL didAddMethod = + class_addMethod(cls, + originSelector, + method_getImplementation(swizzledMethod), + method_getTypeEncoding(swizzledMethod)); + + if (didAddMethod) { + class_replaceMethod(cls, + swizzingSelector, + method_getImplementation(originalMethod), + method_getTypeEncoding(originalMethod)); + } else { + method_exchangeImplementations(originalMethod, swizzledMethod); + } + } +} + +@end diff --git a/AspectsDemo/AspectsDemoTests/AspectsHookTests.m b/AspectsDemo/AspectsDemoTests/AspectsHookTests.m new file mode 100644 index 0000000..f424c45 --- /dev/null +++ b/AspectsDemo/AspectsDemoTests/AspectsHookTests.m @@ -0,0 +1,60 @@ +// +// AspectsHookTests.m +// AspectsDemoTests +// +// Created by Justin.wang on 2018/3/19. +// Copyright © 2018年 PSPDFKit GmbH. All rights reserved. +// + +#import +#import "Aspects.h" +#import "AspectsSwizz.h" + +@interface UIViewController (Hook) + +@end + +@implementation UIViewController (Hook) + +- (void)hook_viewDidAppear:(BOOL)animated { + NSLog(@"Hook viewDidAppear"); + [self hook_viewDidAppear:animated]; +} + +@end + + +@interface AspectsHookTests : XCTestCase + +@end + +@implementation AspectsHookTests + +- (void)testOtherAopFrameworkHookLater { + [[UIViewController class] aspect_hookSelector:@selector(viewDidAppear:) + withOptions:AspectPositionBefore + usingBlock:^(id invoke) { + NSLog(@"Aspects viewDidAppear"); + } error:NULL]; + + [AspectsSwizz swizzingInstanceMethodByClass:[UIViewController class] + originSelector:@selector(viewDidAppear:) + swizzingSelector:@selector(hook_viewDidAppear:)]; + UIViewController *vc = [[UIViewController alloc] init]; + @try { + [vc viewDidAppear:YES]; + XCTAssertTrue(NO, @"viewDidAppear should throw a exception"); + } @catch (NSException *e) { + XCTAssertTrue(YES, @"should catch a exception"); + } + + [AspectManager shareManager].messagePrefixArray= @[@"hook_"]; + @try { + [vc viewDidAppear:YES]; + } @catch (NSException *e) { + XCTAssertTrue(NO, @"should not throw a exception"); + } +} + + +@end