前言
最近刚好看了一篇对masonry这个库做了分析的文章,讲的挺清楚,有兴趣可以参考。
https://github.com/SJ110/analyze/blob/master/contents/Masonry/iOS%20%E6%BA%90%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%90%20---%20Masonry.md
看完之后虽然对于这个链式调用的过程有了一定的认识,但是想要搞清楚还是需要实际跟着这个过程走一下。
正文
以下是对一个uiview构建布局时的masonry代码。下面分析其调用过程。
1 | [self.bottomBackGroundImageView mas_makeConstraints:^(MASConstraintMaker *make) { |
这个方法是定义在uiview的一个分类中的,这种方式便于只需要引入masonry头文件后就可以使用masonry的相关功能。看下内部实现,
1 | - (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block { |
首先是将 translatesAutoresizingMaskIntoConstraints 这个属性设置为NO,这样之后使用autolayout布局才会生效。
然后是初始化了一个MASConstraintMaker对象,这个maker对象初始化时会创建一个narray数组叫做constraints,这个数组就用来保存block中编写的约束内容。用来最后布局。
block中是用来布局约束的代码。
[constraintMaker install] 回去执行block回调中拿到的约束布局数据,去进行布局。
make.left.right.bottom.mas_equalTo(self);
这个链式调用需要重点关注一下,
进入MASConstraintMaker.m看其中的实现,发现其中的约束都是通过调用
addConstraintWithLayoutAttribute来添加的,而每一个方向的约束都会返回一个MASConstraint 对象,所以返回的每个MASConstraint对象都可以继续往下调用,但是其中每一次的调用对于下一次
来说是无感的。
addConstraintWithLayoutAttribute方法最后会调用
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute;
这个方法内会将传递进来的布局属性layoutAttribute 创建一个 MASViewAttribute对象,在用 MASViewAttribute创建一个约束对象 MASViewConstraint。然后将这个约束对象放到之前的数组constraints中。
对于make.left.right.bottom.mas_equalTo(self);来说,前三个链式调用结束之后因为返回的MASConstraint对象,所以继续往下看
1 | - (MASConstraint * (^)(id))equalTo { |
可以看到mas_equalTo和equalTo实现其实是完全一致的。这个方法接受一个id对象,返回一个MASConstraint的block对象,到这里可以说已经很混乱了,先是链式调用,之后又是block类型的方法。
休息一下再看。
继续往下看到内部调用了一个equalToWithRelation的block,这个所以这个block是定义在 MASConstraint+Private.h中,在 MASConstraint.h和其子类中的,之前没注意到我还觉得奇怪,为什么父类中的私有方法会被子类调用。原来是定义在了一个第三方的类中。找到MASConstraint的子类 MASViewConstraint.m中,
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation; 方法也是一个block函数,接受一个id类型和一个 NSLayoutRelation的参数,他会把传进来的两个赋值给MASConstraint的两个属性,然后在返回自身实例。
再看下MASViewConstraint这个类中的一个set方法:
1 | - (void)setSecondViewAttribute:(id)secondViewAttribute { |
可以看到其不仅可以接受一个value类型的变量,也可以接受一个uiview类型的变量和MASViewAttribute类型的变量这也是为什么我们在链式调用时可以mas_equalTo(self),mas_equalTo(20),mas_equalTo(self.mas_left),但是mas_equalTo(self.left)就会报错,因为self.left类型是MASConstraint类型。
这一切动作都结束之后,此时sconstraints数组中已经包含了约束布局数据,进行install,
其中的核心方法时系统提供的对两个视图进行约束的方法。
1 | + (instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c。 |
还会做一些前置判断,比如要约束的视图a,是否能找到到其相对布局的视图b的共同父视图。已经进行过约束,则直接返回,需要更新约束,将新的约束替换旧的约束等等。到此工作才算完全结束。
总结:和自己直接去读源码相比,有了一定的调用链路,加上别人的分析之后,在看源码变得方便许多。这是一个不错的学习源码的方式。不过不要完全把别人的观点带过来,别人的观点也可能出错。
再有就是masonry的代码确实有点复杂。看的头大