OC | 链式API学习

作者: 无夜之星辰 | 来源:发表于2018-03-23 11:38 被阅读482次

实际开发中常见的链式API

使用masonry时经常会见到链式API,如

[@[mvcButton, mvvmButton] mas_makeConstraints:^(MASConstraintMaker *make) {
    make.left.equalTo(@100);
    make.right.equalTo(self.view).offset(-100);
}];

中的:

make.right.equalTo(self.view).offset(-100);

链式API由“点”链接而成,“点”的出现说明这里可能有三种情况:

  1. 后者是前者的属性
  2. 调用了get方法
  3. 调用了set方法

很显然这里没有调用set方法。

那么上面那句代码就可以理解为:

  1. make的属性的属性怎么怎么了...
  2. make调用get方法再调用get方法...
  3. make的属性调用get方法...

分析masonry

为了弄清楚到底属于哪种情况,来看一下masonry源码:

先看make.right

makeMASConstraintMaker类的对象

MASConstraintMaker.h可以看到:

@property (nonatomic, strong, readonly) MASConstraint *right;

MASConstraintMaker.m可以看到:

- (MASConstraint *)right {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight];
}

也就是说right是make的属性,make.right调用了重写后的get方法,返回一个MASConstraint对象

再看right.equalTo(self.view)

MASConstraint.h中并没有equleTo这个属性,但是有一个方法:

/**
 *  Sets the constraint relation to NSLayoutRelationEqual
 *  returns a block which accepts one of the following:
 *    MASViewAttribute, UIView, NSValue, NSArray
 *  see readme for more details.
 */
- (MASConstraint * (^)(id attr))equalTo;

MASConstraint.m中可以看到这个get方法被重写:

- (MASConstraint * (^)(id))equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}

这个方法的返回值是一个block:MASConstraint * (^)(id attr),而这个block的返回值也是MASConstraint对象。

同样的,.offset(-100)也是调用了一个重写后的get方法,返回的一个block,这个block的返回值同样是MASConstraint对象:

/**
 *  Modifies the NSLayoutConstraint constant
 */
- (MASConstraint * (^)(CGFloat offset))offset;
- (MASConstraint * (^)(CGFloat))offset {
    return ^id(CGFloat offset){
        self.offset = offset;
        return self;
    };
}

完整解读

make.right.equalTo(self.view).offset(-100);可以理解为:

makeright属性(MASConstraint对象)调用了重写后的get方法equleTo,返回经过处理后的MASConstraint对象,再调用get方法offset,返回再次经过处理的MASConstraint对象。

如何仿写链式API?

经过上述分析可知链式API的关键在于:

  1. 重写get方法
  2. get方法的返回值是一个block
  3. 这个block的返回值是原对象,这样就可以将get方法一直执行下去了

基于此,我仿写了一个,最终的效果是:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    Dog *dog = [[Dog alloc] init];
    dog.weight = 100; // 最初有100斤
    
    NSLog(@"dog当前体重:%ld", dog.weight);
    
    // 注:dog每跑一千米体重减一斤,每吃一pound体重增加一斤
    dog.eat(10).run(12).run(34).eat(5).eat(1).eat(3).eat(4);
    
    NSLog(@"dog当前体重:%ld", dog.weight);
}

Dog.h文件:

#import <Foundation/Foundation.h>

@class Dog;
typedef Dog *(^RunBlock)(NSInteger distance);
typedef Dog *(^EatBlock)(NSInteger pound);

@interface Dog : NSObject

@property (nonatomic, assign) NSInteger weight;

- (RunBlock)run;
- (EatBlock)eat;

@end

Dog.m文件:

#import "Dog.h"

@implementation Dog

- (RunBlock)run {
    return ^(NSInteger distance) {
        // 每次run,体重相应减少
        self.weight -= distance;
        return self;
    };
}

- (EatBlock)eat {
    return ^(NSInteger pound) {
        // 每次eat,体重相应增加
        self.weight += pound;
        return self;
    };
}

@end

demo

https://github.com/CaiWanFeng/ChainProgramming

以上就是我对链式API的理解,若有差池,欢迎指出。

相关文章

网友评论

本文标题:OC | 链式API学习

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