aop 和 swizzmethod
面向切面编程aop
将各个模块中的重复操作部分,比如权限检查,日志记录,性能统计,安全控制等提取出来进行封装。将业务逻辑处理和日志等代码分离。
处理方法:可以通过预编译和runtime时动态添加功能实现。对于ios来说可以利用ios的runtime进行方法替换和执行。
适用对象
- 减少切面(模块中重复的部分)开发量,最好的例子就是日志。
- 减少耦合,将日志等分离出来
- 提高review质量?
弊端:
1. 寻找log代码比较繁琐
使用举例
给nsmutablearray 添加安全处理机制。
@implementation NSMutableArray (safe)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
id obj = [[self alloc] init];
[obj swizzleMethod:@selector(addObject:) withMethod:@selector(safeAddObject:)];
[obj swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(safeObjectAtIndex:)];
[obj swizzleMethod:@selector(insertObject:atIndex:) withMethod:@selector(safeInsertObject:atIndex:)];
[obj swizzleMethod:@selector(removeObjectAtIndex:) withMethod:@selector(safeRemoveObjectAtIndex:)];
[obj swizzleMethod:@selector(replaceObjectAtIndex:withObject:) withMethod:@selector(safeReplaceObjectAtIndex:withObject:)];
});
}
- (void)safeAddObject:(id)anObject
{
if (anObject) {
[self safeAddObject:anObject];
}else{
NSLog(@"obj is nil");
}
}
- (id)safeObjectAtIndex:(NSInteger)index
{
if(index<[self count]){
return [self safeObjectAtIndex:index];
}else{
NSLog(@"index is beyond bounds ");
}
return nil;
}
- (void)swizzleMethod:(SEL)origSelector withMethod:(SEL)newSelector
{
Class class = [self class];
Method originalMethod = class_getInstanceMethod(class, origSelector);
Method swizzledMethod = class_getInstanceMethod(class, newSelector);
BOOL didAddMethod = class_addMethod(class,
origSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
newSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
统计viewwillappear
使用swizzmthod替换viewwillappear的实现。这样的话就可以在所有的类中执行替换了。
#import "UIViewController+swizzling.h"
#import <objc/runtime.h>
@implementation UIViewController (swizzling)
+ (void)load
{
SEL origSel = @selector(viewDidAppear:);
SEL swizSel = @selector(swiz_viewDidAppear:);
[UIViewController swizzleMethods:[self class] originalSelector:origSel swizzledSelector:swizSel];
}
//exchange implementation of two methods
+ (void)swizzleMethods:(Class)class originalSelector:(SEL)origSel swizzledSelector:(SEL)swizSel
{
Method origMethod = class_getInstanceMethod(class, origSel);
Method swizMethod = class_getInstanceMethod(class, swizSel);
//class_addMethod will fail if original method already exists
BOOL didAddMethod = class_addMethod(class, origSel, method_getImplementation(swizMethod), method_getTypeEncoding(swizMethod));
if (didAddMethod) {
class_replaceMethod(class, swizSel, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
} else {
//origMethod and swizMethod already exist
method_exchangeImplementations(origMethod, swizMethod);
}
}
- (void)swiz_viewDidAppear:(BOOL)animated
{
NSLog(@"I am in - [swiz_viewDidAppear:]");
//handle viewController transistion counting here, before ViewController instance calls its -[viewDidAppear:] method
//需要注入的代码写在此处
[self swiz_viewDidAppear:animated];
}
@end
Aspect
- 安全检查
- 日志打印
比如如果你要在一个storeclient中只处理用户购买相关的操作,但是现在就要加入一些log的东西,这样就会难于维护并且代码重复性严重。
for example
在viewcontroller 里面进行hook,而不必使用继承的方法,避免代码量
@implementation UIViewController (DismissActionHook)
// Will add a dismiss action once the controller gets dismissed.
- (void)pspdf_addWillDismissAction:(void (^)(void))action {
PSPDFAssert(action != NULL);
[self aspect_hookSelector:@selector(viewWillDisappear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo) {
if ([aspectInfo.instance isBeingDismissed]) {
action();
}
} error:NULL];
}
@end