变更记录
方案名称 Runtime - 使用 Aspects 实现 Method Swizzling 和 AOP 实践
关键字 Runtime \ Method Swizzling \ Aspect Oriented Programming \ 面向切面编程
需求场景
利用 Method Swizzling 对系统方法进行替换和追加行为
参考链接
顾 鹏 - Method Swizzling 和 AOP 实践 (推荐)
GitHub - steipete/Aspects
详细内容 在 Objective-C 中, Aspect Oriented Programming (面向切面编程) 就是利用 Runtime 特性给指定的方法添加自定义代码。有很多方式可以实现 AOP ,Method Swizzling 就是其中之一。而且幸运的是,目前已经有一些第三方库可以让你不需要了解 Runtime ,就能直接开始使用 AOP 。
Aspects 就是一个不错的 AOP 库,封装了 Runtime , Method Swizzling 这些黑色技巧,只提供两个简单的API:
1 2 3 4 5 6 7 8 9 10 11 + (id<AspectToken>)aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error; - (id<AspectToken>)aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error;
示例 : 用户看到某个 View 的时候,就把这个事件记下来 1. 通过 Runtime 提供的 Method Swizzling 方法实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @implementation UIViewController (Logging) void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector) { // the method might not exist in the class, but in its superclass Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); // class_addMethod will fail if original method already exists BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); // the method doesn’t exist and we just added one if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } } + (void)load { swizzleMethod([self class], @selector(viewDidAppear:), @selector(swizzled_viewDidAppear:)); } - (void)swizzled_viewDidAppear:(BOOL)animated { // call original implementation [self swizzled_viewDidAppear:animated]; // Logging [Logging logWithEventName:NSStringFromClass([self class])]; } @end
要先尝试添加原 selector 是为了做一层保护,因为如果这个类没有实现 originalSelector ,但其父类实现了,那 class_getInstanceMethod 会返回父类的方法。这样 method_exchangeImplementations 替换的是父类的那个方法,这当然不是你想要的。所以我们先尝试添加 orginalSelector ,如果已经存在,再用 method_exchangeImplementations 把原方法的实现跟新的方法实现给交换掉
2. 通过 Runtime 提供的替换 IMP 方法直接用新的 IMP 取代原 IMP 来实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void (*gOriginalViewDidAppear) (id,SEL,BOOL); @implementation UIViewController (Logging) void newViewDidAppear(UIViewController *self, SEL _cmd, BOOL animated) { // call original implementation gOriginalViewDidAppear(self, _cmd, animated); // Logging [Logging logWithEventName:NSStringFromClass([self class])]; } + (void)load { Method originalMethod = class_getInstanceMethod(self, @selector(viewDidAppear:)); gOriginalViewDidAppear = (void *)method_getImplementation(originalMethod); if(!class_addMethod(self, @selector(viewDidAppear:), (IMP) newViewDidAppear, method_getTypeEncoding(originalMethod))) { method_setImplementation(originalMethod, (IMP) newViewDidAppear); } } @end
2. 通过 Aspects 提供接口实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @implementation UIViewController (Logging) + (void)load { [UIViewController aspect_hookSelector:@selector(viewDidAppear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo) { NSString *className = NSStringFromClass([[aspectInfo instance] class]); [Logging logWithEventName:className]; } error:NULL]; } @end
效果图 (无)
备注 (无)