iOS应用使用Aspects框架实现类方法的拦截
Aspects简介:
Aspects github地址:https://github.com/steipete/Aspects.git
Aspect是一个基于metho swizzing的iOS函数替换的第三方库,他可以很好的实现勾取一个类或者一个对象的某个方法,这个库对外的两个重要接口声明如下:
通过这两个函数可以很好得对某一个类的所有实例和某一个实例的对应方法进行拦截。一个很好的使用场景,公司产品要求,要对用户的页面轨迹进行统计,也即用户每次进入到一个控制器调用viewWillAppear函数时进行相应的处理,现在有以下处理方式。
1、在每一个自定义的控制器的viewWillAppear函数添加处理代码
2、自定义一个集成于UIViewController的基类,在viewWillAppear添加处理代码,然后只有所有的需要统计的控制器都继承自这个基类控制器。
3、使用Aspects来勾取UIViewController类的viewWillAppear方法,在勾取的函数里添加代码
其中方式1代码重复太多,需要修改的地方较多,不易维护,方式2由于要所有的类都继承自自定义基类,需要额外的沟通成本,不可取,方式三可以使用自定义类并重写+ load方法来实现功能,所有以前写的代码不用修改一行,只需继承自UIViewController就可实现功能,这种思想有个专业术语叫AOP,即面向切片编程。
网络上对于Aspects和method swizzing使用的帖子很多,基本用法我也无需赘述,在使用这个库和method swizzing时我都遇到一个问题:如何实现对一个类的类方法(常说的+方法)进行替换?之前我以为使用Aspects无法实现这个功能,因为毕竟接口只是实现了对-方法和某一对象的-方法进行拦截。
于是我开始自己封装了一个基于method swizzing的库文件,利用运行时的知识来对类的类方法来进行替换,demo的地址:https://github.com/2446886848/ZHMethodSwizzingDemo.git实现起来效果还不错,但是毕竟Aspects是经过众多开发者考验的一个方式,而且作者考虑的全面性和稳定性也必然会高很多。于是我开始试着实现使用Aspects,配合少许的代码来实现对类方法的替换。
思路:无论是类的实例对象也好,类对象也好,从运行时的角度来说都是对象,只不过他们在使用的过程中被人们区别化了罢了。既然类对象也是对象,它的方法存在于metal类(元类)中,那么对于metal类来说,类对象也只是单纯的对象,只要我像对普通的实例方法那样来处理类对象,那么实现替换类的方法也是可行的。
废话不多说,拿代码说话!
首先导入Aspects框架到工程,可以使用cocoapod也可用直接拖源代码的方式导入。
因为要使用运行时的东西,接着导入运行时头文件#import
自定义一个类,类名为“Cat”,类中声明了一个类方法+ (void)classFee;
实现+ (void)classFee
{
NSLog(@"Cat classFee");
}
然后开始使用Aspects框架了,代码如下:
Class catMetal = objc_getMetaClass(NSStringFromClass(Cat.class).UTF8String);
[catMetal aspect_hookSelector:@selector(classFee) withOptions:AspectPositionAfter usingBlock:^(idaspectInfo){
NSLog(@"aspectFee");
} error:NULL];
接着在函数里面直接调用[Cat classFee]运行程序,可在输出控制台看到输出结果
至此,即可实现使用Aspects实现类方法拦截。