AOP: 面向切面编程,偏向于处理业务的某个阶段

适用场景:

  1. 参数校验:网络请求前的参数校验,返回数据的格式校验等等

  2. 无痕埋点:统一处理埋点,降低代码耦合度

  3. 页面统计:帮助统计页面访问量

  4. 事务处理:拦截指定事件,添加触发事件

  5. 异常处理:发生异常时使用面向切面的方式进行处理

  6. 热修复:AOP可以让我们在某方法执行前后或者直接替换为另一段代码,我们可以根据这个思路,实现bug修复

  我们希望将以上需求分离到非业务逻辑的方法中,尽可能的不影响业务逻辑的代码。

demo 从配置AOP到实际应用,有空给咱点个star~

源码分析

  0. 类说明

 MDAspectInfo:作为对象,包含调用信息(NSInvocation)的对象
         作为协议,提供访问对象的属性
 MDAspectIdentifier:包含一个hook的信息,调用者,时机,回调处理等
 MDAspectTracker:防止重复hook
 MDAspectsContainer:通过runtime给被hook的对象添加属性,提供存储和移除hook的方法
 MDAspectToken:提供移除hook的协议

  1. hook时机

typedef NS_OPTIONS(NSUInteger, MDAspectOptions) {
MDAspectPositionAfter = , /// 默认,当原方法执行完调用
MDAspectPositionInstead = , /// 替换原方法
MDAspectPositionBefore = , /// 原方法执行前调用 MDAspectOptionAutomaticRemoval = << /// Will remove the hook after the first execution.
};

  2. 配置文件

配置hook的类,hook时机,实例方法和类方法,以及回调处理

为了区分实例方法和类方法,需要在类方法前加一个“+”

+(NSDictionary *)AOP_MDViewControllerConfigDic{

    NSDictionary *configDic = @{
@"MDViewController":@{//hook那个类名
@"TrackEvents":@[
@{//实例方法
@"moment":@"before",//hook之前调用
@"EventSelectorName":@"instanceMethod",//实例方法名
@"block":^(id<MDAspectInfo>aspectInfo){//回调处理
// 获取方法的参数
NSLog(@"跳转");
},
},
@{//类方法
@"moment":@"instead",//替换原方法
@"EventSelectorName":@"+hookClassMethod",//类方法名
@"block":^(id<MDAspectInfo>aspectInfo){//回调处理
// 获取方法的参数
NSLog(@"到处可以hook到我");
},
},
]
},
};
return configDic;
}

  3. 解析管理类  

// hook到方法回调,完全控制
typedef void (^AspectEventBlock)(id<MDAspectInfo> aspectInfo); @implementation MDAOPManager +(void)load{ // 加载配置文件
NSMutableDictionary *mutableDic = [NSMutableDictionary dictionary];
[mutableDic addEntriesFromDictionary:[MDAOPManager AOP_MDViewControllerConfigDic]];
[mutableDic addEntriesFromDictionary:[MDAOPManager AOP_MDSecViewControllerConfigDic]];
[self configAOPWithDic:mutableDic]; } +(void)configAOPWithDic:(NSDictionary *)configDic{
// 解析配置文件
for (NSString *className in configDic) {
Class clazz = NSClassFromString(className);//拿到类名
NSDictionary *config = configDic[className];//配置信息
NSArray *trackArr = config[@"TrackEvents"];//方法数组
if (trackArr) {
for (NSDictionary *event in trackArr) { AspectEventBlock buttonBlock = event[@"block"];//回调
NSString *method = event[@"EventSelectorName"];//方法名
NSString *moment = event[@"moment"];//hook时机 MDAspectOptions option = MDAspectPositionAfter;
if ([moment isEqualToString:@"before"]) {
option = MDAspectPositionBefore;
}else if ([moment isEqualToString:@"instead"]){
option = MDAspectPositionInstead;
} SEL selector = NSSelectorFromString(method); if ([method hasPrefix:@"+"]) {//hook类方法
method = [method substringFromIndex:];
selector = NSSelectorFromString(method); [clazz aspect_hookClassSelector:selector withOptions:option usingBlock:^(id<MDAspectInfo> aspectInfo) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
buttonBlock(aspectInfo);
});
} error:NULL];
}else{//hook实例方法 [clazz aspect_hookSelector:selector withOptions:option usingBlock:^(id<MDAspectInfo> aspectInfo) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
buttonBlock(aspectInfo);
});
} error:NULL];
}
}
}
}
}

4. 对外接口

// 类直接调用,hook实例方法
+ (id<MDAspectToken>)aspect_hookSelector:(SEL)selector withOptions:(MDAspectOptions)options usingBlock:(id)block error:(NSError **)error;
// 对象调用,hook实例方法
- (id<MDAspectToken>)aspect_hookSelector:(SEL)selector withOptions:(MDAspectOptions)options usingBlock:(id)block error:(NSError **)error;
// 类直接调用,hook类方法
+ (id<MDAspectToken>)aspect_hookClassSelector:(SEL)selector withOptions:(MDAspectOptions)options usingBlock:(id)block error:(NSError *__autoreleasing *)error;

// 对象调用,hook类方法
- (id<MDAspectToken>)aspect_hookClassSelector:(SEL)selector withOptions:(MDAspectOptions)options usingBlock:(id)block error:(NSError *__autoreleasing *)error;

说明:MDAspect是对Aspects的扩展,添加了hook类方法的支持,希望能够帮助大家~







iOS AOP实战的更多相关文章

  1. 包建强的培训课程(11):iOS Runtime实战

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...

  2. Cordova - 使用Cordova开发iOS应用实战3(添加Cordova控制台插件)

    Cordova - 使用Cordova开发iOS应用实战3(添加Cordova控制台插件) 前文介绍了通过 Safari 的 Web检查器,可以看到控制台输出的信息.但有时这样调试代码不太方便,如果在 ...

  3. Cordova - 使用Cordova开发iOS应用实战2(生命周期、使用Safari调试)

    Cordova - 使用Cordova开发iOS应用实战2(生命周期.使用Safari调试) 前文我们创建了一个简单的Cordova项目,结构如下: 1,Cordova生命周期事件 (1)device ...

  4. Cordova - 使用Cordova开发iOS应用实战1(配置、开发第一个应用)

    Cordova - 使用Cordova开发iOS应用实战1(配置.开发第一个应用) 现在比较流行使用 html5 开发移动应用,毕竟只要写一套html页面就可以适配各种移动设备,大大节省了跨平台应用的 ...

  5. iOS开发——实战OC篇&环境搭建之Xib(玩转UINavigationController与UITabBarController)

    iOS开发——实战OC篇&环境搭建之Xib(玩转UINavigationController与UITabBarController)   前面我们介绍了StoryBoard这个新技术,和纯技术 ...

  6. iOS开发——实战OC篇&环境搭建之纯代码(玩转UINavigationController与UITabBarController)

    iOS开发——实战OC篇&环境搭建之纯代码(玩转UINavigationController与UITabBarController)   这里我们就直接上实例: 一:新建一个项目singleV ...

  7. rabbitmq在ios中实战采坑

    1. rabbitmq在ios中实战采坑 1.1. 问题 ios使用rabbitmq连接,没过多久就断开,并报错.且用android做相同的步骤并不会报错,错误如下 Received connecti ...

  8. iOS逆向实战与工具使用(微信添加好友自动确认)

    iOS逆向实战与工具使用(微信添加好友自动确认) 原文链接 源码地址 WeChatPlugin-iOS Mac OS 版微信小助手(远程控制.消息防撤回.自动回复.微信多开) 一.前言 本篇主要实现在 ...

  9. Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序

    Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序 C#原本是用来编写Windows以及Windows Phone的应用程序.自从Xamarin问世后.C#的作用就发生了非常大的变化 ...

随机推荐

  1. Servlet和Tomcat底层源码分析

    Servlet 源码分析   Servlet 结构图 Servlet 和 ServletConfig 都是顶层接口,而 GenericServlet 实现了这两个顶层接口,然后HttpServlet ...

  2. [TimLinux] JavaScript 判断 input checkbox选中的方法

    1. input属性 <label> <span>选择</span> <input type="checkbox" name=" ...

  3. 2018HDU多校训练一 A - Maximum Multiple

    Given an integer nn, Chiaki would like to find three positive integers xx, yy and zzsuch that: n=x+y ...

  4. ARTS-S EN0002-London HIV patient's remission spurs hope for curing AIDS

    原文 A stem-cell treatment put a London cancer patient's HIV into remission, marking the second such r ...

  5. python 2.7编码问题

    问题引入 先看下面的代码,代码用utf8编码格式保存. print("中") 仅有一行代码,但是这个代码无论在ubuntu下还是win7下都会报错,错误信息类似是下面的内容: Sy ...

  6. 【JS】394- 简明 JavaScript 函数式编程-入门篇

    转载自公众号"程序员成长指北" 写在开头 本文较长,总共分为三大部分:(对于函数式编程以及其优点有一定理解的童鞋,可以直接从 第二部分 开始阅读) 第一部分:首先会通过实际代码介绍 ...

  7. 11个点让你的Spring Boot启动更快

    前言 使用的是 OpenJDK 11. java --version openjdk 11.0.1 2018-10-16 OpenJDK Runtime Environment 18.9 (build ...

  8. ActiveMQ消息队列从入门到实践(4)—使用Spring JMS收发消息

    Java消息服务(Java Message Service ,JMS)是一个Java标准,定义了使用消息代理的通用API .在JMS出现之前,每个消息代理都有私有的API,这就使得不同代理之间的消息代 ...

  9. oracle创建索引

    数据库索引是为了提高查询速度的一种数据结构. 索引的创建语句 索引的创建语句非常简单. CREATE INDEX 索引名 ON 表名(列名); 除了单列索引,还可以创建包含多个列的复合索引. CREA ...

  10. 松软科技Web课堂:JavaScript JSON

    JSON 是存储和传输数据的格式. JSON 经常在数据从服务器发送到网页时使用. 什么是 JSON? JSON 指的是 JavaScript Object Notation JSON 是轻量级的数据 ...