Swift_技巧(3)_Aspects切面编程应用

一丶介绍

Aspect Oriented Programming(AOP),面向切面编程

AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。

[1] 比如我们最常见的就是日志记录了,举个例子,我们现在提供一个服务查询学生信息的,但是我们希望记录有谁进行了这个查询。如果按照传统的OOP的实现的话,那我们实现了一个查询学生信息的服务接口(StudentInfoService)和其实现类(StudentInfoServiceImpl.java),同时为了要进行记录的话,那我们在实现类(StudentInfoServiceImpl.java)中要添加其实现记录的过程。这样的话,假如我们要实现的服务有多个呢?那就要在每个实现的类都添加这些记录过程。这样做的话就会有点繁琐,而且每个实现类都与记录服务日志的行为紧耦合,违反了面向对象的规则。那么怎样才能把记录服务的行为与业务处理过程中分离出来呢?看起来好像就是查询学生的服务自己在进行,但却是背后日志记录对这些行为进行记录,并且查询学生的服务不知道存在这些记录过程,这就是我们要讨论AOP的目的所在。AOP的编程,好像就是把我们在某个方面的功能提出来与一批对象进行隔离,这样与一批对象之间降低了耦合性,可以就某个功能进行编程。
---摘自百度百科

我们要用AOP来做什么呢?
1.搭建Control的时候,一般会写个BaseViewController,然后把相同功能的代码放在相同的函数内比如以下:

- (void)viewDidLoad
{
    [super viewDidLoad];
    //创建视图代码
    [self createUI];
   //初始化数据
    [self initdata];
   //网络请求
    [self askNetwork];
}

规范代码,可以减少不同开发者之间沟通成本,以及提高问题的定位速度,减少解决时间;
鉴于BaseViewController太臃肿,有了aop编程,就有了新的解决方案;

二丶Aspects的介绍

OC 有一个成熟的aspect方案->Aspects
地址:https://github.com/steipete/Aspects
源码解析及应用:http://wereadteam.github.io/2016/06/30/Aspects/

主要还是用到oc神奇的runtime机制,动态的改变了 selector 和 IMP 的对应关系;(此图并非原创)

Swift_技巧(3)_Aspects切面编程应用_第1张图片
Paste_Image.png

主要用到2个方法:

+ (id)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)aspect_hookSelector:(SEL)selector
                           withOptions:(AspectOptions)options
                            usingBlock:(id)block
                                 error:(NSError **)error;

block 执行的时机

typedef NS_OPTIONS(NSUInteger, AspectOptions) {
    AspectPositionAfter   = 0,            /// Called after the original implementation (default)
    AspectPositionInstead = 1,            /// Will replace the original implementation.
    AspectPositionBefore  = 2,            /// Called before the original implementation.
    AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution.
};

三丶Aspect OC应用

@implementation UIViewController (Base)
+ (void)load
{
    NSError *error = nil;
    [self aspect_hookSelector:@selector(viewDidLoad) withOptions:AspectPositionBefore usingBlock:^(id aspectInfo){
        
        UIViewController *baseVc = [aspectInfo instance];
        [baseVc createUI];
        [baseVc initdata];
        [baseVc askNetwork];
    }error:&error];
    if (error)
    {
        Log(@"Load error: %@",error);
    }
}
@end

这么写成Categorys其实不用BaseViewcontrol也是可以的;只要导入#import "UIViewController+Base.h" 文件就可以;优化不是一点点;

四丶Swift 实现

首先要先知道几个东西;
1.swift 没有load方法,使用initialize()
2.@convention关键字的作用:
2.1 修饰 Swift 中的函数类型,调用 C 的函数时候,可以传入修饰过 @convention(c) 的函数类型,匹配 C 函数参数中的函数指针。
2.2 修饰 Swift 中的函数类型,调用 Objective-C 的方法时候,可以传入修饰过 @convention(block) 的函数类型,匹配 Objective-C 方法参数中的 block 参数
3.unsafeBitCast
unsafeBitCast是非常危险的操作,它会将一个指针指向的内存强制按位转换为目标的类型。因为这种转换是在Swift的类型管理之外进行的,因此编译器无法确保得到的类型是否确实正确,你必须明确地知道你在做什么

4.需要把原先填写block的参数,转成AnyObject

具体代码:

override public class func initialize() {
    /*
     @convention
     1. 修饰 Swift 中的函数类型,调用 C 的函数时候,可以传入修饰过 @convention(c) 的函数类型,匹配 C 函数参数中的函数指针。
     2. 修饰 Swift 中的函数类型,调用 Objective-C 的方法时候,可以传入修饰过 @convention(block) 的函数类型,匹配 Objective-C 方法参数中的 block 参数
     */
    let block: @convention(block) (AnyObject!) -> Void = {
        info in
        let aspectInfo = info as! AspectInfo
        
        let control = aspectInfo.instance()
        #需要判类,
        if let myVc = control as? BaseViewController{
            myVc.customView()
            myVc.createUI()
            myVc.askNetwork()
        }
    }
    #block转AnyObject
    let blobj: AnyObject = unsafeBitCast(block, to: AnyObject.self)
    do {
        let originalSelector = NSSelectorFromString("viewDidLoad")
       #在viewDidLoad之后调用
        try UIViewController.aspect_hook(originalSelector, with: .positionBefore, usingBlock: blobj)
        
    } catch  {
        print("error = \(error)")
    }
}

你可能感兴趣的:(Swift_技巧(3)_Aspects切面编程应用)