目录

1.1 什么是AOP?

1.2 Aspects如何使用?

1.3 虽然不想说,但是总有同学会问,为啥我hook类的方法就不会成功呢?难道这个著名的框架就只能hook实例方法吗?

1.4 使用Aspects需要注意的问题

1.1 什么是AOP?

开发中总会遇到这样的需求,需要对某一个类的所有方法进行统一的操作,例如需要统计用户在每个控制器中停留的时间,大概的实现方法有但不限于以下几种:

  1. 使用category为类添加方法,然后在每个控制器的相关方法调用;
  2. 在控制器基类中添加相关统计的方法,然后让每个控制器继承该控制器基类;
  3. 使用运行时的方法Hook相关方法,添加自己的实现.

针对上面的方法,1)需要手动添加的地方太多,不易维护,而且对于大型项目来讲,手动添加调用很容易遗漏;2)需要额外的沟通成本,耦合严重,在一定程度上破坏了类的封装性;3)可以实现不修改原始类的实现无入侵式改变应用行为,相对来讲,实现简单,易于维护。之前我们聊过使用运行时hook方法实现的原理,我们针对的就是在需要的某一个类或实例中添加一些我们自己的实现,只针对某个切面进行Hook操作,这个就是面向切面的概念(AOP,aspect-oriented programming),针对这个概念有一个非常著名的框架Aspects.

Aspects是一个面向切面编程轻量级类库,主要用于在切面中添加或者已有实现,该类库提供了可选的options选项,来确定执行自定义实现的时机.

1.2 Aspects如何使用?

这个类库的api的也非常简单,只有两个主要的方法

/**全局替换某个类所有的方法实现@param selector 原始方法的sel@param options 执行block时机选项,可以在原始方法执行前,执行后,或者进行替换@param block 需要注入的方法执行@param error 如果出现异常,则该值不为空@return 返回服从AspectToken协议的对象,可以进行移除等操作*/
+ (id<AspectToken>)aspect_hookSelector:(SEL)selectorwithOptions:(AspectOptions)optionsusingBlock:(id)blockerror:(NSError **)error {return aspect_add((id)self, selector, options, block, error);
}
/**替换某个类对象的方法实现@param selector 原始方法的sel@param options 执行block时机选项,可以在原始方法执行前,执行后,或者进行替换@param block 需要注入的方法执行@param error 如果出现异常,则该值不为空@return 返回服从AspectToken协议的对象,可以进行移除等操作*/
- (id<AspectToken>)aspect_hookSelector:(SEL)selectorwithOptions:(AspectOptions)optionsusingBlock:(id)blockerror:(NSError **)error {return aspect_add(self, selector, options, block, error);
}

需要注意的是,block的方法实现默认第一个参数为block对象自己,而OC的方法默认第一个参数为当前的对象第二个参数为当前调用方法的SEL,这样的话OC的方法实现转化为block时就会少一个SEL参数,所以在Aspects中作者添加了一个很有意思的操作,将原始实现的相关信息进行封装(服从AspectInfo协议的对象),当block携带参数时,将id<AspectInfo>通过block调用进行返回.这么做的好处:

  • 使OC方法对应的block实现具有相同数量的参数,在进行方法参数匹配和调用赋值等需要遍历匹配参数时非常便利;
  • 可以在需要的时候调用原始的方法实现,尤其是option选项是AspectPositionInstead时,可以根据自己的需要选择执行原始实现的时机.

所以当定义Block时可以根据自己的需要来选择是否显式携带参数:如果在自定义的Block的实现中不需要原始的实现信息(比如知识需要一个时机来做事件统计),则可以将Block定义为不显式携带参数实现:

void(^block)(void) = ^void(void){
//your code here!};

而更多的时候在自定义信息中需要原始实现的信息,例如:

  • option选项是AspectPositionInstead时,需要根据需求在执行自定义实现之前/只后执行原始操作;
  • 需要用到原始的对象的相关信息,例如统计时需要用到控制器的名字信息等;
  • 在某种情况下,是否执行原始操作以及执行原始实现的时机不确定,需要根据特定条件进行判断;

这种情况下就需要将原始的实现信息通过Block进行传递.

//实现一:只需要原始实现的部分信息
void(^)(id<AspectInfo> info) = ^(id<AspectInfo> info){
//your code here
};
//实现三:需要原始实现的完整参数
void(id<AspectInfo> info,...) = ^(id<AspectInfo> info,...){
//your code here!
};

在实现需求统计每个控制器的展示时,就可以通过

void(^block)(id<AspectInfo>) = ^(id<AspectInfo> info){[TrackingManager screenView:NSSttringFromClass([info.instance class])];
};//或者
void(^block)(id<AspectInfo>, BOOL) = ^(id<AspectInfo> info, BOOL animated){[TrackingManager screenView:NSSttringFromClass([info.instance class])];
};[UIViewController aspect_hookSelector:@selector(viewDidAppear:) withOptions:(AspectPositionBefore) usingBlock:block error:&error];

在block中可以接收协议的AspectInfo协议的对象,用以获取到当前被hook的实例对象,参数列表,以及对原始方法进行封装的NSInvocation对象等信息,可以对当前对象进行相关操作.

1.3  Aspects是否可以hook类方法?

在之前的文章中,我们谈论过OC中类与元类之间的关系,其中有聊到这样的知识点:实例方法其实并不保存在实例对象中,而是保存在类的结构中,而类方法并不保存在类中,而是保存在类的元类中。这样的设计,使得同一类的实例对象没有必要都保存一份实例方法的备份,使用时只需要去类的结构中获取方法实现,并传入实例对象的参数即可,极大节约了内存空间,同时使得方法查找回溯更有效率.所以,需要想要hook类方法,就要去对应的元类中进行hook.

@interface Person : NSObject
/**定义一个需要hook的类方法@param str 参数@return 返回值*/
+ (NSString *)combineDescription:(NSString *)str;@end
@implementation Person
+ (NSString *)combineDescription:(NSString *)str {return @"I like China!";
}
@end//同样在应用启动时,进行hook
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {NSError *error = nil;//可以使用object_getClass方法传入对应的类id metalClass =  object_getClass([Person class]);//也可以使用objc_getMataClass传入对应类的c字符串//    id metalClass = objc_getMetaClass(@"Person".UTF8String);[metalClass aspect_hookSelector:@selector(combineDescription:) withOptions:AspectPositionAfter usingBlock:^(id <AspectInfo> info){NSLog(@"info == %@", info.arguments);} error:&error];[Person combineDescription:@" Here "];// Override point for customization after application launch.return YES;
}

1.4  AOP主要有哪些应用场景?

AOP在开发中是一个非常重要的思想,我们希望将需求分离到非业务逻辑的方法中,尽可能的不影响业务逻辑的代码。主要的应用场景大概有以下几种:

  • 参数校验:网络请求前的参数校验,返回数据的格式校验等等;
  • 无痕埋点:统一处理埋点,降低代码耦合度;
  • 页面统计:帮助统计页面访问量;
  • 事务处理:拦截指定事件,添加触发事件;
  • 异常处理:发生异常时使用面向切面的方式进行处理;
  • 热修复:AOP可以让我们在某方法执行前后或者直接替换为另一段代码,我们可以根据这个思路,实现bug修复.

1.5 使用Aspects需要注意的问题

1.5.1 性能问题

在Aspects中我们找到了这样一段话:

/**Aspects uses Objective-C message forwarding to hook into messages. This will create some overhead. Don't add aspects to methods that are called a lot. Aspects is meant for view/controller code that is not called a 1000 times per second.Adding aspects returns an opaque token which can be used to deregister again. All calls are thread safe.*/

Aspects利用的OC的消息转发机制去hook消息,会有额外的系统开销,不要尝试把Aspects添加到高频率调用的方法中去,Aspects设计用来hook view/controller方法,而不是给那些一秒调用1000次的方法使用的.所以在可能的情况下,不要将高频率地调用Aspects的hook方法.不过线程是安全的,可以放心使用.

1.5.2 Aspects是不是可以hook全部的方法?

并不是,Aspects有一个sel的"黑名单",要求

disallowedSelectorList = [NSSet setWithObjects:@"retain", @"release", @"autorelease", @"forwardInvocation:", nil];

所以forwardInvocation:不能被hook,这是因为Aspects主要就是使用了objc_msgForward来实现的,在下一节中我们会聊到.

1.5.3 如果我需要hook类中的dealloc方法,有什么注意点?

如果需要hook类中的dealloc方法,则有一个非常重要的点需要留意,那就是AspectOptions参数只能选择AspectPositionBefore这个应该不用解释了吧.

1.5.4 多次调用Aspects hook同一个方法,会重复hook执行多次吗?

答案是不是,Aspects里有一个全局的字典存储hook过的方法,所以同一个方法不会hook多次.

1.5.5  在x86-64模拟器上使用时可能会出现闪退问题?

Aspects中关于将需要hook方法的实现实现指向_objc_msgForward还是_objc_msgForward_stret的处理有瑕疵,导致在64位处理器的模拟器上会出现闪退问题.详细原因见 Aspects(三) 3.5.

了解了Aspects的基本使用,下一次我们将探索关于Aspects的源码实现.

Aspects框架------使用相关推荐

  1. Spring事务和Aspects框架管理事务,看这篇就够了!(简单易懂!)

    Spring事务和Aspects框架管理事务的用法 一.事务的介绍 1.1什么是事务 1.2事务的四大特性 二.事务的隔离级别以及导致的问题介绍 2.1隔离级别介绍 2.2导致的问题介绍 2.2.1脏 ...

  2. Aspects框架------汲取

    之前我们聊过关于Aspects源代码解析,今天简单聊一聊这个类库中我们可以学习到那些实用的开发技巧. 3.1 如何获取block签名 在apple最新的开源代码中我们可以找到关于block的定义: / ...

  3. AOP框架Aspects原理解析

    先说下我的读源码过程 , 总计花了1天时间 , 早上还是一脸懵 , 下午就是成竹在胸了 1 .先看了这2篇文章 , 知道了大体的思路, 找到了源码的骨头架子, https://www.jianshu. ...

  4. iOS AOP框架Aspects实现原理

    前言 众所周知,Aspects框架运用了AOP(面向切面编程)的思想,这里解释下AOP的思想:AOP是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之 ...

  5. Spring框架Runtime介绍(导包)

    一.Spring框架Runtime介绍,如图 1.1 Test: Spring提供测试功能 1.2 Core Container:Spring核心容器,Spring启动的基本条件, 1.2.1 Bea ...

  6. 【Spring】Spring学习笔记完整篇

    文章目录 Spring 1.简介 优点 组成 弊端 2.IOC 控制反转 控制什么? 谁来控制? 反转? 依赖注入DI? IOC和DI关系 IOC本质 3.hello spring Applicati ...

  7. iOS AOP 框架 - Aspects 源码解读

    Aspects 是什么? Aspects 是 iOS 上的一个轻量级 AOP 库.它利用 method swizzling 技术为已有的类或者实例方法添加额外的代码,它是著名框架 PSPDFKit ( ...

  8. ABP框架详解(六)Aspects

    这种AOP式的设计非常类似于Asp.net MVC和WebApi中过滤器(Filter)机制,感觉没有太多可讲述的,只能谈谈设计思路. 框架中AspectAttribute特性用于设置到需要被拦截的T ...

  9. 【Spring】框架简介

    [Spring]框架简介 Spring是什么 Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IOC(Inverse Of Control:反转控制)和AOP(Asp ...

最新文章

  1. 基于图像到UV Map映射的3D手部高保真重建网络(ICCV2021)
  2. 有关网页渲染,每个前端开发者都该知道的那点事
  3. 【控制】多智能体系统总结。5.系统合并。
  4. xgboost学习率不能大于1的原因
  5. nssl1254-A(林下风气)【树形dp】
  6. uva 1394poj 3517
  7. our happy ending(状压dp)
  8. MIUI10迎来最后一波开发版推送 用户体验再升级
  9. axios_的请求响应结果的结构---axios工作笔记006
  10. 深入Pthread(五):线程属性
  11. mac的rubywoo怎么读_macrubywoo是几号
  12. java系统性能优化之mysql数据库优化
  13. Android建快捷方式app,创建快捷方式最新版下载-创建快捷方式appv1.17 安卓版-腾牛安卓网...
  14. 黑客攻防技术宝典浏览器实战篇
  15. php paypal支付接口文档,php 实现PayPal支付
  16. VSCODE mac版下载慢解决办法
  17. phpMyAdmin安装配置教程
  18. 6个免费、免版权视频素材网站
  19. Word文档如何生成目录?
  20. Android中MVP模式

热门文章

  1. JavaEE——网络原理_应用层
  2. C++实现w3cshool设计模式教程--设计模式中的Java代码
  3. tedu斌-MySql笔记2112-1
  4. 中国31省市区42部门投入产出表(1997-2017年)
  5. 【240期】面试官问:说说基于 Redis 实现延时队列服务?
  6. 点聚焦透镜平台的使用
  7. 2018,微信公众号可能要变天了
  8. 互联网应用中UGC、PGC、OGC三大内容生产方式详解
  9. 循环神经网络(RNN)的工作方式(一)
  10. 求阶乘N!末尾0的个数