iOS AOP实战
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实战的更多相关文章
- 包建强的培训课程(11):iOS Runtime实战
@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...
- Cordova - 使用Cordova开发iOS应用实战3(添加Cordova控制台插件)
Cordova - 使用Cordova开发iOS应用实战3(添加Cordova控制台插件) 前文介绍了通过 Safari 的 Web检查器,可以看到控制台输出的信息.但有时这样调试代码不太方便,如果在 ...
- Cordova - 使用Cordova开发iOS应用实战2(生命周期、使用Safari调试)
Cordova - 使用Cordova开发iOS应用实战2(生命周期.使用Safari调试) 前文我们创建了一个简单的Cordova项目,结构如下: 1,Cordova生命周期事件 (1)device ...
- Cordova - 使用Cordova开发iOS应用实战1(配置、开发第一个应用)
Cordova - 使用Cordova开发iOS应用实战1(配置.开发第一个应用) 现在比较流行使用 html5 开发移动应用,毕竟只要写一套html页面就可以适配各种移动设备,大大节省了跨平台应用的 ...
- iOS开发——实战OC篇&环境搭建之Xib(玩转UINavigationController与UITabBarController)
iOS开发——实战OC篇&环境搭建之Xib(玩转UINavigationController与UITabBarController) 前面我们介绍了StoryBoard这个新技术,和纯技术 ...
- iOS开发——实战OC篇&环境搭建之纯代码(玩转UINavigationController与UITabBarController)
iOS开发——实战OC篇&环境搭建之纯代码(玩转UINavigationController与UITabBarController) 这里我们就直接上实例: 一:新建一个项目singleV ...
- rabbitmq在ios中实战采坑
1. rabbitmq在ios中实战采坑 1.1. 问题 ios使用rabbitmq连接,没过多久就断开,并报错.且用android做相同的步骤并不会报错,错误如下 Received connecti ...
- iOS逆向实战与工具使用(微信添加好友自动确认)
iOS逆向实战与工具使用(微信添加好友自动确认) 原文链接 源码地址 WeChatPlugin-iOS Mac OS 版微信小助手(远程控制.消息防撤回.自动回复.微信多开) 一.前言 本篇主要实现在 ...
- Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序
Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序 C#原本是用来编写Windows以及Windows Phone的应用程序.自从Xamarin问世后.C#的作用就发生了非常大的变化 ...
随机推荐
- 【django后端分离】rbac组件(文件源代码+使用)
1:用户,角色,权限,菜单表设计 from django.db import models # 用户菜单 class UserMenu(models.Model): title = models.Ch ...
- jQuery中的层级选择器
话不多说,请看效果: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> &l ...
- [TimLinux] CSS 计数功能实现递归目录
内容引用自<css世界>: count-reset 与 counter 为父子关系,兄弟关系会导致序号混乱 调用一次 count-increment 将给序号进行一次报数,调用 count ...
- CodeForces-Round524 A~D
A. Petya and Origami time limit per test 1 second memory limit per test 256 megabytes input stan ...
- HDU-1595Find the longest of shortest(最短路径的最长路Dijkstra+记录路径)
Marica is very angry with Mirko because he found a new girlfriend and she seeks revenge.Since she do ...
- python做单因素方差分析
方差分析的主要功能就是验证两组样本,或者两组以上的样本均值是否有显著性差异,即均值是否一样. 这里有两个大点需要注意:①方差分析的原假设是:样本不存在显著性差异(即,均值完全相等):②两样本数据无交互 ...
- 基于 HTML5 + WebGL 的 3D 太阳系系统
前言 近年来随着引力波的发现.黑洞照片的拍摄.火星上存在水的证据发现等科学上的突破,以及文学影视作品中诸如<三体>.<流浪地球>.<星际穿越>等的传播普及,宇宙空间 ...
- django----csrf跨站请求伪造 auth组件 settings源码 importlib模块
目录 importlib模块 csrf跨站请求伪造 form表单发送 ajax发送 csrf装饰器 auth模块 如何创建超级用户(root) 创建用户 校验用户名和密码是否正确 保存用户登录状态 判 ...
- Test 1022
T1 AERODROM (二分答案 TimeLimit: 1000MS Memory Limit: 32768KB \(N\)个登机口,办理登机业务,第\(i\)个窗口的单位办理时间为\(T_i\), ...
- VS2019 开发Django(一)------环境配置
导航:VS2019开发Django系列 缘起:学习是我一直在做的一件事情,但是,可怕的是不知道学习什么,然后止步不前,安于现状,曾经很长的一段时间,我是不知道学习什么,工作上的事情,其实是相对固定的, ...