
链式编程
特点一
- 链式编程可以通过点语法
.调用函数。
例如 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:
|
|
案例三
|
|
时序图

|
|