分析源码:
分析源码前想想怎么看源码:
- 目的:这个第三方库,他是为了解决什么问题的?
- 技术起源:效果实现的核心技术是什么?
- 设计模式:整体结构是什么样的?
- 该怎么入手:由外而内逐层分析层次结构?
Masonry 是什么
- Masonry是一个轻量级的布局框架,它以更好的语法包装了AutoLayout
- Masonry拥有自己的布局DSL,它提供了可链式语法来描述NSLayoutConstraints,从而使布局代码更简洁易读。
(DSL 其实是 Domain Specific Language 的缩写,中文翻译为领域特定语言)
为什么要设计 Masonry
对比一下 AutoLayout 中使用 NSLayoutConstraint
是多么糟糕
1 | UIView *superview = self.view; |
如果使用 Masonry
1 | [view1 mas_makeConstraints:^(MASConstraintMaker *make) { |
P.S. Masonry 内部会帮你调用 view.translatesAutoresizingMaskIntoConstraints = NO;
Masonry 设计的目的,就是以链式编程手段封装 NSLayoutConstraint
冗余代码!
- 如何处理原来繁杂接口
- 如何实现链式
- 如何封装
NSLayoutConstraint
的 - 使用了何种编程手段整体架构设计,设计模式,语法技巧
带着这些问题,根据源码结构分析出来
Masonry 代码结构分析
从调用层次分析
先根据函数调用一层一层的找
- 最外层接口
根据这段代码分析
UIView category 中实现 View+MASAdditions.h (View+MASShorthandAdditions.h)
1 |
|
- mas_left, mas_right …… 三个见名知意的方法
- 方法参数
MASConstraintMaker
这个就是 make - 属性
MASViewAttribute
这个就是 make 后面的 make.edges id mas_key
这个是啥,目前不知道
我想知道,他的链式是怎么实现的,如何封装 autolayout 相关的数据的,目前看到的只是平常使用的接口
我还要找到下图的这些信息目前看不出来端倪view formula
- 接着往里面看
- MASViewAttribute: 一个不可变元组,存着 receiver view, related view 和关联的
NSLayoutAttribute
[根据文档注释]
1 | @property (nonatomic, weak, readonly) MAS_VIEW *view; |
目前理解为下图等式的相关信息
- MASConstraintMaker:
提供一个工厂方法来创建MASConstraints
, 这些 constraints 只有在被 installed 的时候才会被收集根据文档注释
MASConstraintMaker.h
1 | @property (nonatomic, strong, readonly) MASConstraint *left; |
- MASConstraint?跟
NSLayoutConstraint
好像- 允许使用可链接的语法创建约束
- 约束可以表示单个 NSLayoutConstraint(MASViewConstraint)
- 或一组 NSLayoutConstraints(MASComposisteConstraint)
bingo,到这里下面这两个问题有眉目了, 最开始的几个问题
1 | 2. 如何实现链式 |
分析 Masonry
链式封装
主要组件结构
- MASConstraintMaker
- MASConstraint
- MASViewConstraint
- MASComposisteConstraint
- MASConstraintDelegate
使用函数调用栈分析
1 | [redView mas_makeConstraints:^(MASConstraintMaker *make) { |
整体流程&核心调用栈分析结束后,接下来再看细节技巧就容易多了
代码组件分析
- MASConstraintMaker
- MASConstraint
- MASViewConstraint
- MASComposisteConstraint
- MASConstraintDelegate
- MASViewAttribute
NSLayoutConstraint
的问题- 布局数据分散,使用命令式方法调用,接口参数就是布局属性,导致用户使用闹心
Masonry 是如何解决这个问题的呢?
- 需要创建对象保存 model 数据
- 创建链节点(把大量参数以链节点的方式逐一放在节点中(MASViewConstraint节点))
- 调用系统
NSLayoutConstraint
方法
整体步骤:
- node1: 创建 MASConstraint 节点,绑定第一个 item1 和 attr1
- node2: 创建 MASViewAttribute(item2, attr2),并添加等式的 relation (block 闭包调用)
- node3: 添加 valueOffset,priority,divideBy …… 常量数据 (block 闭包低啊用)
- block 结束,所有 constraint 配置完,进行 install 内部调用
NSLayoutConstraint
方法,把复杂的方法封装在内部
model 层数据
创建一个 constraint 需要:
- 两对(view, attr)
- relation
- mulity
- constant
- priority
- MASViewAttribute 元组封装 (item, constraintAttr)
- MASViewConstraint 在 MASViewAttribute 基础上封装 (mas_attr = 1.0 * mas_attr + constant)
生成 MASViewAttribute 的方式
- UIView+MASAdditions
1 | // 计算属性 |
MASConstraintMaker
生成链条节点MASConstraint
时,使用工厂方法内部配置MASViewAttribute
MASConstraint 配置过程
make.bottom.equalTo(blueView.mas_top).offset(-padding);
- attr1: 由 MASConstraintMaker 中的计算属性方法创建 make.top.left
- relation: 由 MASConstraint 中的方法 equalTo 添加
- attr2: 由 UIView+MASAdditions 扩展方法得到
- offset: 由 MASConstraint 中的方法 equalTo 添加
- priority: 由 MASConstraint 中的方法 priority 添加
布局工厂 MASConstraintMaker
他的 product 是 MASConstraint
- 他的工厂方法
1 | // 1. 计算属性,我理解为工厂方法 |
得出结论:
- 根据上面代码中的 2.1,2.2 知道 make.left.right.top…… 会生产 3 个 MASViewConstraint,并组合到 MASCompositeConstraint 里面
2333 这就知道了 为什么要有 MASCompositeConstraint,他的一个作用就是 实现链式兼容啊!(因为赤裸裸的 3个 MASViewConstraint,怎么变成一个 节点啊)
那么问题又来了,make.left.right.top(view),是怎么实现 left = view.left, right = view.right …… 的呢?
- 等式关系,使用函数表示,内部直接把对应 relation 配置给 constraint
1 | - (MASConstraint * (^)(id attr))mas_equalTo; |
- 接下来的链式都是用函数闭包的方式,把参数传给 constraint 的 [闭包格式 MASConstraint * (^)(parameter 参数)]
- MASConstraintDelegate 是工厂方法协议, 从 这段代码的 2 看,他是让 constraint 生成 constraint 的方法过渡点。
- 他是为了满足 make.top.left 中 top.left 通过 topConstraint 生成一个 leftConstraint的方法
- 所以对于链式编程技巧:
- 链条的开始,创建成链的起始节点对象
- 一条链上应该只有一个对象,后面的所有节点都是对于这个对象的属性配置添加
- 如果同一条链上有多个对象,那么使用组合模式,把多个节点对象封装成一个节点对象
1 | 为什么要使用闭包传?使用方法消息传不好吗? |
其他开发细节分析
下面这些是啥?
都是一些 c 语言的宏,在其他文章里分析过了
1 | //MASUtilities.h |
runtime 的运用
主要是给 view 添加关联对象
1 | //MASViewConstraint.m |
mas_key 是为了调试布局用的