经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » iOS » 查看文章
iOS AOP实战
来源:cnblogs  作者:liyongjie  时间:2019/8/26 8:41:36  对本文有异议

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

适用场景:

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

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

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

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

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

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

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

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

源码分析

  0. 类说明

 

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

 

  1. hook时机

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

  2. 配置文件

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

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

  1. +(NSDictionary *)AOP_MDViewControllerConfigDic{
  2. NSDictionary *configDic = @{
  3. @"MDViewController":@{//hook那个类名
  4. @"TrackEvents":@[
  5. @{//实例方法
  6. @"moment":@"before",//hook之前调用
  7. @"EventSelectorName":@"instanceMethod",//实例方法名
  8. @"block":^(id<MDAspectInfo>aspectInfo){//回调处理
  9. // 获取方法的参数
  10. NSLog(@"跳转");
  11. },
  12. },
  13. @{//类方法
  14. @"moment":@"instead",//替换原方法
  15. @"EventSelectorName":@"+hookClassMethod",//类方法名
  16. @"block":^(id<MDAspectInfo>aspectInfo){//回调处理
  17. // 获取方法的参数
  18. NSLog(@"到处可以hook到我");
  19. },
  20. },
  21. ]
  22. },
  23. };
  24. return configDic;
  25. }

  3. 解析管理类  

  1. // hook到方法回调,完全控制
  2. typedef void (^AspectEventBlock)(id<MDAspectInfo> aspectInfo);
  3. @implementation MDAOPManager
  4. +(void)load{ // 加载配置文件
  5. NSMutableDictionary *mutableDic = [NSMutableDictionary dictionary];
  6. [mutableDic addEntriesFromDictionary:[MDAOPManager AOP_MDViewControllerConfigDic]];
  7. [mutableDic addEntriesFromDictionary:[MDAOPManager AOP_MDSecViewControllerConfigDic]];
  8. [self configAOPWithDic:mutableDic];
  9. }
  10. +(void)configAOPWithDic:(NSDictionary *)configDic{
  11. // 解析配置文件
  12. for (NSString *className in configDic) {
  13. Class clazz = NSClassFromString(className);//拿到类名
  14. NSDictionary *config = configDic[className];//配置信息
  15. NSArray *trackArr = config[@"TrackEvents"];//方法数组
  16. if (trackArr) {
  17. for (NSDictionary *event in trackArr) {
  18. AspectEventBlock buttonBlock = event[@"block"];//回调
  19. NSString *method = event[@"EventSelectorName"];//方法名
  20. NSString *moment = event[@"moment"];//hook时机
  21. MDAspectOptions option = MDAspectPositionAfter;
  22. if ([moment isEqualToString:@"before"]) {
  23. option = MDAspectPositionBefore;
  24. }else if ([moment isEqualToString:@"instead"]){
  25. option = MDAspectPositionInstead;
  26. }
  27. SEL selector = NSSelectorFromString(method);
  28. if ([method hasPrefix:@"+"]) {//hook类方法
  29. method = [method substringFromIndex:1];
  30. selector = NSSelectorFromString(method);
  31. [clazz aspect_hookClassSelector:selector withOptions:option usingBlock:^(id<MDAspectInfo> aspectInfo) {
  32. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  33. buttonBlock(aspectInfo);
  34. });
  35. } error:NULL];
  36. }else{//hook实例方法
  37. [clazz aspect_hookSelector:selector withOptions:option usingBlock:^(id<MDAspectInfo> aspectInfo) {
  38. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  39. buttonBlock(aspectInfo);
  40. });
  41. } error:NULL];
  42. }
  43. }
  44. }
  45. }
  46. }

4. 对外接口

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

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

总结

核心步骤:把目标selector的IMP更换为runtime中的IMP,从而直接进入消息转发,检查是否能添加hook,如果能,进行存储,接着方法交换处理,在消息转发里运行before instead after方法

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

原文链接:http://www.cnblogs.com/lyjpost/p/11402024.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号