美文网首页
objective-c DSL的实现思路

objective-c DSL的实现思路

作者: 我是繁星 | 来源:发表于2018-09-03 19:27 被阅读0次

    背景

    我们都用过Masonry等三方库,其链式变成的形式使代码更简洁、更语义化的表达自己的逻辑。这种用于特定领域的表达方式叫做DSL(Domain Specific Language),本文就介绍一下DSL的实现思路。

    举个例子我们来创建一个UIView,设置其frame、背景色,并添加至父View上,是这个样子的。

        UIView * aView = [[UIView alloc]initWithFrame: aFrame];
        aView.backgroundColor = aColor;
        [self.view addSubview:aView];
    

    再看下链式编程的表达式,

    UIView *view = AllocA(UIView).with.position(20,20).size(50,50).bgColor(UIColor.redColor).intoView(self.view);
    

    语义分析

    链式变成从语义上更像我们平常的句子。包含宾语(动作接收者)、助词(起连贯作用)、定语(修饰词)。当然要实现上面的形式必须借助一个中间类,假定为ViewMaker。


    语义分析-3.png

    实现

    关键字:block、宏、中间类。
    实现DLS、第一步就遇到麻烦了,OC中调用方法都需要用中括号表示,如何不用中括号去调用方法?
    答案是在类外部声明,并实现,这个类不依存于类对象,所以无法调用self。在该方法中创建了viewMaker对象,并将所类型保存至viewMaker对象中。

    ViewMaker * alloc_a(Class aClass){
        ViewMaker * maker = ViewMaker.new;
        maker.viewClass = aClass;
        return maker;
    }
    

    这样我们可以实现直接调用

    alloc_a([UIView class]);
    

    但是我们必须传入一个类对象[UIView class]的形式,又用到了中括号,这里可以用神奇的宏解决这个问题

    //根据宏的对应关系,只需要直接传类型即可,不用传类对象。
    #define AllocA(aClass) alloc_a([aClass class]);
    

    这里我们用很简洁的形式传入了宾语,并保存至中间对象中,返回用作进一步操作。

      AllocA(UIView);
    

    为了语法连贯需要一些助词来帮助,这里只是为了语义连贯。返回中间类自身就可以。

    //通过get方法返回
    - (ViewMaker *)with{
        return self;
    }
    //调用
      AllocA(UIView).with
    

    定语也就是修饰词用block来实现,这里传参过程用block来实现,用属性形式保存在viewMaker对象中,因为要实现链式变成,所以要把viewMaker对象返回回来,这样修饰词也保存完毕。

    @property (nonatomic, assign) CGPoint myPosition;
    @property (nonatomic, assign) CGSize mySize;
    @property (nonatomic, strong) UIColor *color;
    @property (nonatomic, copy) ViewMaker * (^position)(CGFloat x, CGFloat y);
    @property (nonatomic, copy) ViewMaker * (^size)(CGFloat width, CGFloat height);
    @property (nonatomic, copy) ViewMaker * (^bgColor)(UIColor *color);
    
    - (instancetype)init{
        if (self = [super init]){
            __weak __typeof(self) wSelf = self;
            _position = ^ViewMaker * (CGFloat x, CGFloat y){
                wSelf.myPosition = CGPointMake(x, y);
                return wSelf;
            };
            _size = ^ViewMaker * (CGFloat width, CGFloat height){
                wSelf.mySize = CGSizeMake(width, height);
                return wSelf;
            };
            _bgColor = ^ViewMaker * (UIColor *color){
                wSelf.color = color;
                return wSelf;
            };
        return self;
    }
    

    终结词:这个词在现代语法中是找不到的,但是在DSL中是很重要的,ViewMaker的实例从头到收集了很多的修饰,在最后的表达式中产生了结果,本例中就是根据类型创建对象,根据修饰属性给对象初始化赋值,并返回。

    代码

    //.h文件
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    #define AllocA(aClass) alloc_a([aClass class])
    
    @interface ViewMaker : NSObject
    @property (nonatomic, readonly) ViewMaker * with;
    @property (nonatomic, strong) Class viewClass;
    @property (nonatomic, copy) ViewMaker * (^position)(CGFloat x, CGFloat y);
    @property (nonatomic, copy) ViewMaker * (^size)(CGFloat width, CGFloat height);
    @property (nonatomic, copy) ViewMaker * (^bgColor)(UIColor *color);
    @property (nonatomic, copy) UIView * (^intoView)(UIView *superView);
    @end
    ViewMaker * alloc_a(Class aClass);
    
    
    //.m文件
    #import "ViewMaker.h"
    @interface ViewMaker()
    @property (nonatomic, assign) CGPoint myPosition;
    @property (nonatomic, assign) CGSize mySize;
    @property (nonatomic, strong) UIColor *color;
    @end
    @implementation ViewMaker
    - (instancetype)init{
        if (self = [super init]){
            __weak __typeof(self) wSelf = self;
            _position = ^ViewMaker * (CGFloat x, CGFloat y){
                wSelf.myPosition = CGPointMake(x, y);
                return wSelf;
            };
            _size = ^ViewMaker * (CGFloat width, CGFloat height){
                wSelf.mySize = CGSizeMake(width, height);
                return wSelf;
            };
            _bgColor = ^ViewMaker * (UIColor *color){
                wSelf.color = color;
                return wSelf;
            };
            
            _intoView = ^UIView * (UIView * superView){
                CGRect rect = CGRectMake(wSelf.myPosition.x, wSelf.myPosition.y, wSelf.mySize.width, wSelf.mySize.height);
                viewClass * view = [[viewClass alloc] initWithFrame:rect];
                view.backgroundColor = wSelf.color;
                [superView addSubview:view];
                return view;
            };
        }
        return self;
    }
    - (ViewMaker *)with{
        return self;
    }
    @end
    ViewMaker * alloc_a(Class aClass){
        ViewMaker * maker = ViewMaker.new;
        maker.viewClass = aClass;
        return maker;
    }
    
    
    //调用
     UIView *view = AllocA(UIView).with.position(20,20).size(50,50).bgColor(UIColor.redColor).intoView(self.view);
    

    总结

    通过类外函数创建中间类,通过中间类的block属性传入参数保存并返回中间类,最终通过终结词返回最终创建的对象。这条链的传递的是中间类,最终通过中间类保存的描述信息返回最终对象。
    臧成威老师博客

    相关文章

      网友评论

          本文标题:objective-c DSL的实现思路

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