iOS的AOP(面向切面)编程--Aspects
2017-05-07 15:05
483 查看
什么是AOP呢?下面是来自百科的一段话:
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
综上所述:面向切面编程就是通过预编译和运行期动态代理实现给程序动态统一添加功能的一种技术。
我们什么情况下可能会用到AOP呢?比如如果需要在每个控制器的
这时可能很容易想到
如果对
库的核心代码就两个方法:
上面两个方法就是实现对类的某些方法进行拦截。比如我们要在每个控制器的
上面代码实现了拦截所有控制器的viewDidLoad事件。
1、
2、
3、
上面的代码运行可以看出,只要任何控制器对象或者其子类的
比如我们封装一个类似网易的菜单栏,效果如下图所示:
菜单栏
如果实现上面的效果,首先创建一个类,比如名字是
DLMenuView
在
在
上面实现不需要写任何代码,点击每个选项卡我们会调用这个方法,并且会传递2个参数:第一个是
当定义完成后,我们就可以侦听点击的是第几个选项卡了。我们可以在控制器里面这样用:
上面就实现了侦听
上面方法可以减少类与类的耦合度,我们可以在不设置回调的情况下就能很方便的满足需求。有时候,回调很多或者嵌套对我们处理某些问题带来很多麻烦。总之我们采用的任何处理方式或者设计模式等方案,都需要考虑尽量减少类与类之间的耦合,使类更好用。
最后附上Demo: 下载地址
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
综上所述:面向切面编程就是通过预编译和运行期动态代理实现给程序动态统一添加功能的一种技术。
我们什么情况下可能会用到AOP呢?比如如果需要在每个控制器的
viewDidLoad里面都需要添加统计代码,或者每个类都需要添加日志代码。其实上面的需求很容易想到在每个控制器里面都写一遍,这样的话会有很多重复的代码而且不好维护。另外也可以用继承,但是用继承无形中增加了类的强耦合,所以都不是最好的办法。
这时可能很容易想到
runtime中
method swizzle。
method swizzle是runtime的黑魔法之一,也就是在无法看到一个类的源代码的情况下,改变方法实现或者偷换方法实现的一种强大技术。
method swizzle确实是一个很好的方法,而且降低了业务逻辑各个部分的耦合性。
Aspects
如果对method swizzle不太了解也没有关系,不过
github上有比较成熟的第三方库,Aspects下载地址 。
Aspects就是基于
method swizzle的第三方库,用的就是AOP面向切面编程思想。
库的核心代码就两个方法:
/// Adds a block of code before/instead/after the current `selector` for a specific class. /// /// @param block Aspects replicates the type signature of the method being hooked. /// The first parameter will be `id<AspectInfo>`, followed by all parameters of the method. /// These parameters are optional and will be filled to match the block signature. /// You can even use an empty block, or one that simple gets `id<AspectInfo>`. /// /// @note Hooking static methods is not supported. /// @return A token which allows to later deregister the aspect. + (id<AspectToken>)aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error; /// Adds a block of code before/instead/after the current `selector` for a specific instance. - (id<AspectToken>)aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error;
上面两个方法就是实现对类的某些方法进行拦截。比如我们要在每个控制器的
viewDidLoad方法中执行
doSomethings方法。可以这样用:
/** * 事件拦截 * 拦截UIViewController的viewDidLoad方法 */ [UIViewController aspect_hookSelector:@selector(viewDidLoad) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo) { //NSLog(@"viewDidLoad调用了 --- %@ --- %@ --- %@",aspectInfo.instance,aspectInfo.arguments, aspectInfo.originalInvocation); NSLog(@"%@ 对象的viewDidLoad调用了",aspectInfo.instance); /** * 添加我们要执行的代码,由于withOptions是AspectPositionAfter。 * 所以每个控制器的viewDidLoad触发都会执行下面的方法 */ [self doSomethings]; } error:NULL]; - (void)doSomethings { //TODO: 比如日志输出、统计代码 NSLog(@"------"); }
上面代码实现了拦截所有控制器的viewDidLoad事件。
1、
aspect_hookSelector:表示要拦截指定对象的方法。
2、
withOptions:是一个枚举类型,
AspectPositionAfter表示
viewDidLoad方法执行后会触发
usingBlock:的代码。
3、
usingBlock:就是拦截事件后执行的自定义方法。我们可以在这个
block里面添加我们要执行的代码。
上面的代码运行可以看出,只要任何控制器对象或者其子类的
viewDidLoad方法触发,
doSomethings方法就会触发。
比如再举一个例子:
比如我们封装一个类似网易的菜单栏,效果如下图所示:菜单栏
如果实现上面的效果,首先创建一个类,比如名字是
DLMenuView
DLMenuView
在
DLMenuView.h里面我们定义一个类别,这个方法就是在外部做事件拦截用的,也就是当我们点击每个选项卡按钮的时候,会调用这个方法。如下所示:
@interface DLMenuView (Aspects) - (void)dlMenuView:(DLMenuView *)menuView atIndex:(NSInteger)atIndex; @end
在
DLMenuView.m里面定义如下:
@implementation DLMenuView (Aspects) - (void)dlMenuView:(DLMenuView *)menuView atIndex:(NSInteger)atIndex { } @end
上面实现不需要写任何代码,点击每个选项卡我们会调用这个方法,并且会传递2个参数:第一个是
DLMenuView对象,第二个是点击的选项卡的索引
atIndex。那不实现这个方法我们写有什么用呢?我们想下,拦截某个事件方法首先我们要告诉其他类哪些方法可以拦截,为了方便,我们使用类别。另外,我们总不能把按钮点击触发事件公开吧,这样设计的类也不是很合理。
当定义完成后,我们就可以侦听点击的是第几个选项卡了。我们可以在控制器里面这样用:
DLMenuView *menuView = [[DLMenuView alloc] initWithFrame:CGRectMake(0, 80, self.view.frame.size.width, 40)]; //菜单栏标题 menuView.menuTitles = @[@"新闻",@"头条",@"好声音",@"原创",@"视频",@"土豆",@"优酷",@"科技",@"图片"]; menuView.backgroundColor = [UIColor yellowColor]; //默认选择第1项 menuView.selectedIndex = 0; [self.view addSubview:menuView]; /** * 拦截menuView的dlMenuView:atIndex:方法 */ [menuView aspect_hookSelector:@selector(dlMenuView:atIndex:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspects, DLMenuView *menuView, NSInteger index) { NSLog(@"按钮点击了 %ld",index); } error:nil];
上面就实现了侦听
dlMenuView:atIndex:的调用,拦截这个方法后获取到的
index就是点击的选项卡的索引。
上面方法可以减少类与类的耦合度,我们可以在不设置回调的情况下就能很方便的满足需求。有时候,回调很多或者嵌套对我们处理某些问题带来很多麻烦。总之我们采用的任何处理方式或者设计模式等方案,都需要考虑尽量减少类与类之间的耦合,使类更好用。
Aspects确实是一个很强大的类,特别对于有添加日志、统计等需求项目来说带来了方便,就不需要我们在每个类里面添加相同的代码,因为添加的这些代码与当前类的业务关联不是很大。
最后附上Demo: 下载地址
相关文章推荐
- Aspects iOS的AOP面向切面编程的库
- Aspects iOS的AOP面向切面编程的库
- Aspects– iOS的AOP面向切面编程的库
- iOS开发-面向切面编程之 Aspects 源码解析
- iOS面向切面编程AOP实践
- 面向切面编程AOP 在iOS中的实现
- iOS面向切面编程-AOP
- 黑马程序员--Spring Aop 面向切面编程,实现前置通知
- Spring AOP 面向切面编程相关注解
- js实现面向切面的编程(AOP)
- Java乔晓松-基于注解的面向AOP(切面)编程
- spring学习笔记8--使用spring进行面向切面的(AOP)编程(2)XML配置方式
- Aop_面向切面编程_思想理解资料02 .
- Aop_面向切面编程_思想理解资料01
- Spring in Action 入门之面向切面编程AOP
- 三、面向切面编程AOP(Aspect oriented Programming)
- AOP面向切面编程
- 深入浅出面向切面编程AOP
- 何为"面向切面编程AOP"
- Spring面向切面编程AOP的个人理解