可能被忽略掉编程技巧 之 链式编程
@author Jou Email Weibo or Github
可能被忽略掉编程技巧 之 链式编程
预热 - 链式编程
哎呦,不错哦。有点时间, 随便写篇文章,聊聊iOS中的链式编程。
链式编程? 如果你不是iOS开发者,相信你应该没听过链式编程。对的, 链式编程,并不是一种编程范式。链式编程这种表述, 应该是在Objective-C独有,因为Objective-C是一门繁琐而奇怪的语言。
在Swift之前,想成为iOS开发者,在入门前你就有了两个门槛: 1. 你要有台苹果, 2. 你要肯于接受Objective-C的反人类,和繁琐。
所以形式上,链式编程是"点方法"的调用,来弥补Objc的繁琐。
Objective-C
NSString objc = @"Hello world";
[objc lowercaseString]; // 给objc 发送lowercaseString的消息
而相比之下swift 就更为简洁
let str = "Hello, playground"
str.lowercaseString;
链式编程 - 点式调用 LinkBlock
为了实现Objc中的点式调用, Novo已经有了一个基于block的好玩的实现LinkBlock。
LinkBlock 旨在帮助我们实现点方法调用的happy path。
如:
UIButton* btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame= CGRectMake(20, 20, 150, 80);
UIColor *color = [UIColor colorWithRed:255/255.0 green:22/255.0 blue:150/255.0 alpha:1.0];
btn.backgroundColor = color;
[btn setTitle:@"click change color" forState:UIControlStateNormal];
[self.view addSubview:btn];
//如果使用链式编程的方式,大部分工作可以在思路连续的情况下进行
//now just using one line.Most work can be wrapped up in the idea of ongoing cases
btn.viewSetFrame(20,20,150,80).viewBGColor(@"0xff22cc".strToColorFromHexStr())
.viewAddToView(self.view).btnTitle(@"click change color", UIControlStateNormal);
是不是简洁了许多。
链式编程 - Masonry
自从iOS设备尺寸多样化以后,为了适配大小屏,我们用到了autolayout。
即便是我们使用了VFL,官方的autolayout写法,在代码实现上也过于繁琐。
所以很大程度上, 一部分人转而重用storyboard界面布局,一部分转而重度依赖mansory, 还有大多数在结合使用。
而Masonry的实现上, 恰恰就是围绕Maker对象的链式编程。
之前autolayout的代码是这样的
UIView *superview = self;
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[superview addConstraints:@[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],
]];
Masonry实现、
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];
显而易见的、真是简洁到没朋友。
ps: Masonry 在github上 已有1W+的star。
除了形式上的链式调用, Objc中所谈的链式调用, 更是函数编程中一种常用的编程手段。
函数式编程 - Function Chaining
在swift中引入了函数式编程之后,把函数编程推到了新的高度。
而在swift中, 偶尔会见到这样的代码。
let result = students |> filterOutFailed |> sortByGrade |> formatForPrint |> joinByComma
把 ‘|>’ 换成 ’.' ,便有了极大的相似并不是偶然, 链式编程,只是函数编程思想的一种结合。
百分百的知识 - 实践 链式编程
如果团队有人在项目中,没有讨论的贸然加入了LinkBlock框架, 我肯定还是会argue一下的。
因过于依赖于LinkBlock,在代码可读性上增加了难度,并且增加了不可预期的崩溃。
如LinkBlock中不能很好的处理,给nil进行链式调用,造成崩溃。
所以我认为,链式编程更好的实践,应该是应用在复用性较好的组件中, 最好还可以像masonry一样,使用block包裹起来。
如, 语句块在objc的使用方式之一:
self.tableView.tableFooterView = ({
UIView *view = [[UIView alloc]initWithFrame:CGRectZero];
view;
});
这不是纯秀技巧, 在代码可读性上, 他的模块, 和逻辑更加清晰。
所以我最近在考虑,将链式编程的思想加入我自己的 开源项目Router, 或 Dispatcher 中。 而形式上,则更类似Masonry的maker。
场景是 : 假如,我买了一架灰机, 我要驾驶它。
使用Block的特性,Trick实现链式调用
那么,我的飞机,一定可以起飞,降落, 上下左右,依照block的特性。我可以这么实现它
- (MAFlight *(^)(CGFloat distence))takeOff
{
return ^MAFlight *(CGFloat distence)
{
//Do move distence
return self;
};
}
- (MAFlight *(^)(CGFloat distence))landDown
{
return ^MAFlight *(CGFloat distence)
{
//Do move distence
return self;
};
}
- (MAFlight *(^)(CGFloat distence))forward
{
return ^MAFlight *(CGFloat distence)
{
//Do move distence
return self;
};
}
- (MAFlight *(^)(CGFloat distence))back
{
return ^MAFlight *(CGFloat distence)
{
//Do move distence
return self;
};
}
- (MAFlight *(^)(CGFloat distence))left
{
return ^MAFlight *(CGFloat distence)
{
//Do move distence
return self;
};
}
- (MAFlight *(^)(CGFloat distence))right
{
return ^MAFlight *(CGFloat distence)
{
//Do move distence
return self;
};
}
所以我们就可以这么调用
MAFlight *flight = [[MAFlight alloc]init];//我买了飞机
flight.takeOff(10.f).forward(100000.f).left(10.f).right(1.f).back(1.f).back(1.f).landDown(10.f);
好了那么我们已经实现了链式调用,但,还没满足于此,因为逻辑还没有更加清晰。 为了叫买飞机的流程,和开飞机的流程更加清晰。实现了maker、
实现Maker
为了叫买飞机,我给NSObject实现了分类+Extra.h
+ (CGFloat)makeFlight:(void (^)(MAFlight *))block {
//购买飞机
MAFlight *maker = [[MAFlight alloc]init];
//操作飞机
block(maker);
return maker.distence;
}
再为飞机加一点语法糖
- (MAFlight *)and
{
return self;
}
所以, 就可以这么玩飞机了
[objc makeFlight:^(MAFlight *flight){
flight.takeOff(10.f).and.forward(100000.f);
flight.left(10.f).and.right(1.f);
flight.back(1.f).and.back(1.f).landDown(10.f)
}];
OK, That's all.
下一篇 - iOS Nightmare系列之 - 客户端应用的Daily Build 与 持续化集成(Continuous Integration)