链式编程
特点一
- 链式编程可以通过点语法
.
调用函数。
例如 Objective-C 中的 getter 方法:
|
|
在 Objective-C 一般使用中括号 []
的方式调用调用函数,但是也可以通过点语法 .
调用函数,但是编译器会提示警告:
|
|
警告如下:
特点二
- 链式编程可以通过点语法
.
连续调用函数。
|
|
要想实现上面这段代码,可以参考链式编程每个方法都有返回值的特点。可以让函数执行完成后返回当前对象 return self
,实现代码如下:
|
|
打印结果:
|
|
上面这段代码,是参考 Objective-C 调用 getter 方法实现的,但是这种方式还比较局限。例如带有参数的函数调用:
|
|
打印结果:
|
|
函数往往是带有参数的,如果需要传入参数,该怎么用点语法.
实现呢?
这个时候 block 就该登场了:
|
|
通过 block 轻松实现了带有参数的 getter 方法,从而满足的点语法调用带参函数的要求。但是这样还不能满足点语法连续调用的特点,如连续调用两个或多个带参函数,该怎么实现呢?
|
|
打印结果:
|
|
根据上面调用一个带参函数的实现,可以想到使用点语法连续调用带参函数的代码应该长这个样子:
|
|
这个时候带有返回值的 block 就该登场了:
|
|
打印结果:
|
|
实现细节:
self.lisi
返回了一个类型为ViewController *(^)(NSString *name)
的 block(参数类型:NSString*
,返回值类型:ViewController*
);- 通过括号
()
来实现 block 的调用,括号内是需要传递的参数,self.lisi(@"name1")
; - block 调用后返回了当前对象
self
,从而实现了点语法.
连续调用,self.lisi(@"name1").wangwu(@"name2")
。
特点三
- 链式编程可以通过点语法
.
无限连续调用函数。
|
|
打印结果:
|
|
Masonry简介
来自 Masonry 说明文档里的示例:创建一个 view,它的上下左右四个边和父视图的四个边相距 10 的距离。
方案一:使用 NSLayoutConstraints
布局的实现代码
|
|
方案二:这是使用 Masonry 布局的实现代码
|
|
可以看得出 Masonry 作者通过举出这个例子想要表达的意思,用 Masonry 布局真的太方便了。
Autolayout
NSLayoutConstraint
上面的例子添加了一个 view 到视图上,该 view 的四个边和 superview 的四个边设置边距为 10,而这每一个边的边距都对应着一个约束对象(NSLayoutConstraint
)。
如上边的约束对象的初始化方法:
|
|
从这段代码不难看出,约束是相对的,至少需要两个 view 才能创建出一个约束对象。可以总结出创建一个约束对象的条件:
- 第一个视图;
- 第一个视图的约束属性(约束方向:上/下/左/右等);
- 第二个视图;
- 第二个视图的约束属性(约束方向:上/下/左/右等);
- 约束关系(等于/大于/小于);
这五个条件是生成一个约束对象所必须的,也是一个约束对象所拥有的最基础的元素。这一点在 NSLayoutConstraint.h
文件中定义的属性也能看出来:
|
|
NSLayoutAttribute
在 NSLayoutConstraint.h
文件中定义的属性中,可以看到两个约束属性 firstAttribute
和 secondAttribute
,它们用来描述视图的那一个方向需要添加约束。NSLayoutAttribute
是一个枚举,定义了视图支持自动布局的各个位置。
|
|
关于带 Margin 的枚举值:
NSLayoutAttributeLeft
表示视图的最左边;NSLayoutAttributeLeftMargin
表示视图的左边,且与父视图的左边具有一个边距(margin),这个值通过layoutMargins
进行修改。
NSLayoutRelation
约束关系,在创建约束对象时,需要明确指出两个视图的约束关系。
|
|
Masonry的UML
图中从左至右把 Masonry 分为三块:
View+MASAdditions
,UIView
的延展,让UIView
更方便的调用 Masonry 提供的属性和方法,这也是三方框架惯用的手段,为无访问权限的源代码扩展能力(逆向建模);MASConstraintMaker
,简称 Maker,是创建约束的工厂方法,即所有类都会以各种方式调用到这个类里,由它来完成创建约束的操作;MASConstraint
,简称 Constant(约束),它是一个抽象父类,关联的子类有:MASViewConstraint
(单个约束)、MASCompositeConstraint
(约束结合),都被 Maker 创建。
因为 Masonry 是采用链式编程的范式构建的,也是就是万物皆可点,一直点一直有(返回值),所以这三个模块包含使用点语法调用的属性或函数。
View+MASAdditions
🤔思考:UIView
通过点语法会返回什么呢?
对 UIView
的延展,主要是为了通过拓展的方式放大接口,给 UIView
增加新的属性和方法:
- 属性创造
MASViewAttribute
,简称 Attribute,约束属性,它创建约束对象的条件之一; - 方法创造
MASConstraintMaker
,简称 Maker。
|
|
定义属性的目的,是想利用属性的 getter 方法,从而实现通过点语法调用函数的方式,创建约束属性。
下面是属性的 getter 方法的具体实现,内部直接返回了一个初始化好的约束属性:
|
|
对 UIView
的延展好处是,为 UIView
增加了 Masonry 自定义的属性和方法,从而使调用变的更加直接,就像是 UIView
原本就具有这些属性和方法一样:
|
|
综上所述,UIView
通过点语法会创建出一个约束方向对象 MASViewAttribute
。
MASViewAttribute
- 这一部分关心的是 Attribute(约束属性),也就是约束方向。
通过上面👆约束属性的初始化方法,不难看出一个 MASViewAttribute
类型的约束属性,至少包含两个东西:
- View:视图;
- Attribute:约束方向。
|
|
从 MASViewAttribute.h
文件里也能看出这一点:
|
|
从定义可以看出 MASViewAttribute
将约束视图 View 和约束属性 Attribute 封装到了同一个类里,相对于 NSLayoutAttribute
枚举来说,MASViewAttribute
创建出来后是一个对象,拥有更多的信息,且更加的面向对象。这也正好弥补了 Objective-C 中枚举地位低的缺点。
MASConstraintMaker
- 这一部分关心的是 Constraint(约束)的创建创建者
MASConstraintMaker
,简称 Maker。
🤔思考:约束创建者 Maker 通过点语法会返回什么呢?
创建约束的工厂方法,负责约束对象的创建工作,即所有类都以代理的方式,调用到这个类里,由它来完成创建约束的操作。
在 UIView 的延展部分提到了 Maker 的创建,通过调用 mas_makeConstraints
方法创建:
|
|
通过初始化代码,可以看出 Maker 首先持有了 view,成为了 view 专属的约束创造者,再通过 block 回调出去收集约束:
|
|
关于 Maker 和 view 是一对一的关系,也可以从 Maker 的定义里看出来:
|
|
Maker 将约束定义为属性,是想利用属性的 getter 方法,通过点语法的方式创建约束,如 left
属性的 getter 方法:
|
|
综上所述,约束创建者 Maker 通过点语法会创建出一个约束对象 MASConstraint
。
关于 Maker 的核心方法 constraint:addConstraintWithLayoutAttribute:
,主要用来创建约束对象,下面以序号的方式演示代码的执行过程:
- 创建单个约束对象
MASViewConstraint
; - 创建约束集合
MASCompositeConstraint
。
|
|
MASConstraint
- 这一部分主要关心的是约束自身,简称 Constraint。
🤔思考:约束是否也是可以点的,通过点语法可以创造出什么呢?
这里的定义和约束创建者 Maker 类似,只不过这里没有写成属性(readonly)的方式,而是直接实现了 getter 方法。
猜想:这样写是因为用了面向对象的封装,属性是对象的特征。Maker 是可以拥有约束特征的,而约束自身不应该再拥有约束特征。如“人”可以有“左手”和“右手”,而“手”自身不再拥有这些特征。
|
|
getter 方法的实现部分 和 Maker 是完全一致的,毕竟想实现的功能是一样的。
|
|
MASConstraint
约束抽象类,与 Maker 一样可以通过点语法创建一个新的约束对象。MASConstraint
真正满足了链式编程要求,即每个方都有返回值,返回值为对象自身。又因为 MASConstraint
是 Maker 的属性,所以 Maker 也满足了链式编程。至此,Masonry 中可以实现链式编程的类有两个 MASConstraint
(及其子类) 和 MASConstraintMaker
。
MASViewConstraint
MASViewConstraint
约束类,是 MASConstraint
的子类,Maker 生成的约束对象就是 MASViewConstraint
类型的。
MASViewConstraint
对标的是 Autolayout 中的 NSLayoutConstraint
,前边通过 NSLayoutConstraint
的定义看出它有 5 个基本条件:
- 第一个视图;
- 第一个视图的约束属性(约束方向:上/下/左/右等);
- 第二个视图;
- 第二个视图的约束属性(约束方向:上/下/左/右等);
- 约束关系(等于/大于/小于);
下面是 MASViewConstraint
的定义,可以看出,它只需要两个条件:
|
|
其实还有一个条件,约束关系,被定义子在了 MASViewConstraint.m
文件。综上所述,可以总结出创建一个 MASViewConstraint
对象的基本条件有三个:
- 第一个视图和第一个视图的约束属性;
- 第二个视图和第二个视图的约束属性;
- 约束关系。
可以看的出,两者的条件内容本质是一样的,只不是实现方式不同而已。MASViewConstraint
使用面向对象的方式,把“视图”和“视图的约束属性”打包成元组的形式进行管理。
🤔思考:把“视图”和“视图的约束属性”打包成元组的操作妙在哪?
MASCompositeConstraint
MASCompositeConstraint
约束集合类,当一个 Maker 创建了一个以上的约束对象时,就会创建出约束集合来管理这些约束。
|
|
Masonry约束实现原理
UIView
通过 mas_makeConstraints
方法为自己添加约束。
案例一
|
|
mas_makeConstraints
的实现:
|
|
调用该方法做了四件事情:
- 设置不支持 AutoresizingMask;
- 初始化专属约束创造者 Maker;
- 收集约束;
- 添加约束(应用约束)。
约束的收集过程:
|
|
时序图
|
|
第一步 make.top
:Maker 创建 view 的 top 约束对象,核心方法 constraint:addConstraintWithLayoutAttribute:
|
|
第一步执行完成后,生成了一个约束对象 MASViewConstraint
,简称 Constraint,同时它已经有了第一个视图和它的约束属性(NSLayoutAttributeTop
)。
🤔思考:make.top
创建出来的 top
约束对象的类型一定是 MASViewConstraint
的吗?
第二步 make.top.equal
:设置依赖关系,首先来到约束抽象类 MASConstraint
里
|
|
这一步有两个参数:
- attribute 约束属性(视图和视图的约束属性);
- relation 约束关系(
NSLayoutRelationEqual
)。
因为上面👆 make.top
返回的是 MASViewConstraint
类型,所以接下来会调用到子类 MASViewConstraint
实现的 equalToWithRelation
方法里:
|
|
第二步执行完成后,Constraint 具备了全部基本条件:
firstViewAttribute
(view1 的约束属性);secondViewAttribute
(view2 的约束属性);- relation(约束关系)。
案例二
|
|
时序图
|
|
第一步 make.top
:同案例一,make.top
返回的永远是 MASViewConstraint
。
|
|
第二步 make.top.left
:
|
|
因为第一步 make.top
返回的是 MASViewConstraint
类型,MASViewConstraint
作为子类重写了父类方法 addConstraintWithLayoutAttribute:
:
|
|
通过代理,再次掉到 Maker 创建约束的方法:
|
|
第三步 make.top.left.right
:
|
|
因为在第二步中 make.top.left
返回的是 MASCompositeConstraint 类型,所以将由 MASCompositeConstraint 来执行接下来的任务:
|
|
负责创建约束对象的方法:
|
|
第四步 make.top.left.right.bottom
和第三步相同
|
|
第五步 .equalTo(superview)
:
|
|
第六步 install
:
|
|
案例三
|
|
时序图
|
|