美文网首页征服iOSSwift好文收藏swift 文章收集
Swift_技巧(3)_Aspects切面编程应用

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

作者: 丶纳凉 | 来源:发表于2017-03-18 22:52 被阅读362次

    一丶介绍

    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 的对应关系;(此图并非原创)

    Paste_Image.png

    主要用到2个方法:

    + (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;
    
    

    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> 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)")
        }
    }
    

    相关文章

      网友评论

      • 高高叔叔:initialize 这个好像只有在2.0的时候才可以用的吧。
        高高叔叔:@丶纳凉 现在应该不会有大动作了。
        丶纳凉:@高高叔叔 emm...swift变化太快了.:sweat:

      本文标题:Swift_技巧(3)_Aspects切面编程应用

      本文链接:https://www.haomeiwen.com/subject/smvpnttx.html