实际开发中常见的链式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由“点”链接而成,“点”的出现说明这里可能有三种情况:
- 后者是前者的属性
- 调用了get方法
- 调用了set方法
很显然这里没有调用set方法。
那么上面那句代码就可以理解为:
- make的属性的属性怎么怎么了...
- make调用get方法再调用get方法...
- make的属性调用get方法...
分析masonry
为了弄清楚到底属于哪种情况,来看一下masonry源码:
先看make.right
make
是MASConstraintMaker
类的对象
在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);
可以理解为:
make
的right
属性(MASConstraint
对象)调用了重写后的get方法equleTo
,返回经过处理后的MASConstraint
对象,再调用get方法offset
,返回再次经过处理的MASConstraint
对象。
如何仿写链式API?
经过上述分析可知链式API的关键在于:
- 重写get方法
- get方法的返回值是一个block
- 这个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的理解,若有差池,欢迎指出。
网友评论