Masonry 源码简单解析
Posted 梦想家-mxj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Masonry 源码简单解析相关的知识,希望对你有一定的参考价值。
Masonry是一个轻量级的用于自动布局的框架,是对系统的自动布局约束的一个封装。
Masonry让NSLayoutConstraint使用起来更为简洁。Masonry简化了NSLayoutConstraint的使用方式,让我们可以以链式的方式为我们的控件指定约束。
上面是Masonry的类图,从类图中我们来整体的分析Masonry框架的结构。然后再由整体到部分逐渐的细化,窥探其内部的实现细节。
masonry框架的类结构
根据上面的类图,我们分别看一下框架里面各个类。
1、UIView的分类 - View+MASAdditions上面红线框住的部分
上图是View+MASAdditions文件中,为UIView添加了类型为MASViewAttribute的成员属性,以mas_left成员属性为例,因为MASViewAttribute是View与NSLayoutAttribute的合体,所以mas_left就代表着当前View的NSLayoutAttributeLeft属性,也就是mas_left存储的是当前View的NSLayoutAttributeLeft属性。
同理,mas_top就代表着当前View的NSLayoutAttributeTop属性,其他成员属性也是一样。
除了添加一系列的成员属性外,还添加了四个公有的方法:mas_closestCommonSuperview方法负责寻找两个视图的最近的公共父视图(类比两个数字的最小公倍数)、mas_makeConstraints方法负责创建安装约束、mas_updateConstraints负责更新已经存在的约束(若约束不存在就Install)、mas_remakeConstraints方法则负责移除原来已经创建的约束并添加上新的约束。上述方式是UIView对象设置约束主要调用的方法,稍后会详细介绍其实现方式。
①
/**
* Finds the closest common superview between this view and another view
*
* @param view other view
*
* @return returns nil if common superview could not be found
*/
- (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view;
这个方法负责寻找两个视图的最近的公共父视图。
它的实现方法:
- (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view
MAS_VIEW *closestCommonSuperview = nil;
MAS_VIEW *secondViewSuperview = view;
while (!closestCommonSuperview && secondViewSuperview)
MAS_VIEW *firstViewSuperview = self;
while (!closestCommonSuperview && firstViewSuperview)
if (secondViewSuperview == firstViewSuperview)
closestCommonSuperview = secondViewSuperview;
firstViewSuperview = firstViewSuperview.superview;
secondViewSuperview = secondViewSuperview.superview;
return closestCommonSuperview;
其中MAS_VIEW是宏,就是UIView。上面的两个while循环中通过遍历两个view的superview,在内部while中判断superview是否相等,进而找出了最近的公共父视图。
②
/**
* Creates a MASConstraintMaker with the callee view.
* Any constraints defined are added to the view or the appropriate superview once the block has finished executing
*
* @param block scope within which you can build up the constraints which you wish to apply to the view.
*
* @return Array of created MASConstraints
*/
- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
这个方法负责创建约束,是不是很熟悉,就是我们外部调用的
[self.view mas_makeConstraints:^(MASConstraintMaker *make)
//约束们
];
mas_makeConstraints的方法实现中
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
block(constraintMaker);
return [constraintMaker install];
首页禁用Autoresizing,使用Autolyout自动布局,创建一个约束制造者constraintMaker,查看MASConstraintMaker中的
initWithView:(MAS_VIEW *)view
的实现
- (id)initWithView:(MAS_VIEW *)view
self = [super init];
if (!self) return nil;
self.view = view;
self.constraints = NSMutableArray.new;
return self;
将view赋值给maker的属性view,另外我们发现
maker的view属性是weak的
-
(这是重点,说明maker没有强引用view,也就是说mas_makeConstraints实现中,block(constraintMaker),对constraintMaker中的MAConstraint类型的属性进行初始化,block
没有强引用view那么这就是我们在外部使用mas_makeConstraints时,不使用weakself的原因了)
,属性constraints是用来保存约束们的数组。
不使用weakself的原因
这个block只是个栈block,而且构不成循环引用的条件。栈block有个特性就是它执行完毕之后就出栈,出栈了就会被释放掉。看mas_makexxx的方法实现会发现这个block很快就被调用了,完事儿就出栈销毁,构不成循环引用,所以可以直接放心的使用self。
https://blog.csdn.net/blackeynes/article/details/78853109
③
/**
* Creates a MASConstraintMaker with the callee view.
* Any constraints defined are added to the view or the appropriate superview once the block has finished executing.
* If an existing constraint exists then it will be updated instead.
*
* @param block scope within which you can build up the constraints which you wish to apply to the view.
*
* @return Array of created/updated MASConstraints
*/
- (NSArray *)mas_updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
负责更新已经存在的约束(若约束不存在就Install)
④
/
**
* Creates a MASConstraintMaker with the callee view.
* Any constraints defined are added to the view or the appropriate superview once the block has finished executing.
* All constraints previously installed for the view will be removed.
*
* @param block scope within which you can build up the constraints which you wish to apply to the view.
*
* @return Array of created/updated MASConstraints
*/
- (NSArray *)mas_remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
责移除原来已经创建的约束并添加上新的约束
③④这两个函数内部的实现与mas_makeConstraints类似,就是多了一个属性的设置。mas_updateConstraints中将constraintMaker中的updateExisting设置为YES, 也就是说当添加约束时要先检查约束是否已经被安装了,如果被添加了就更新,如果没有被添加就添加。而mas_remakeConstraints中所做的事情是将removeExisting属性设置成YES, 表示将当前视图上的旧约束进行移除,然后添加上新的约束
2、工厂类MASConstraintMaker 类图中绿色框住的部分
该类就是一个工厂类,负责创建MASConstraint类型的对象(依赖于MASConstraint接口,而不依赖于具体实现)。在UIView的View+MASAdditions类目中就是调用的MASConstraintMaker类中的一些方法。上述我们在使用Masonry给subView添加约束时,mas_makeConstraints方法中的Block的参数就是MASConstraintMaker的对象。用户可以通过该Block回调过来的MASConstraintMaker对象给View指定要添加的约束以及该约束的值。该工厂中的constraints属性数组就记录了该工厂创建的所有MASConstraint对象。
上图是MASConstraintMaker中的部分属性,可以看出这些属性都是MSAConstrian类型
我们查看MSAConstrian类,上面的意思是MSAConstrian可以是约束们通过链式编程的方式创建出来
约束可以表示一个NSLayoutConstraint(苹果原生约束类),MASViewConstraint是MASConstraint的子类
也就是说MASViewConstraint相当于ios中的NSLayoutConstraint
在maker中
上图是部分属性的getter方法,都调用了
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute
是MASConstraint的私有方法通过代理实现,
在maker的代理实现
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
if ([constraint isKindOfClass:MASViewConstraint.class])
//replace with composite constraint
NSArray *children = @[constraint, newConstraint];
MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
compositeConstraint.delegate = self;
[self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
return compositeConstraint;
if (!constraint)
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];
return newConstraint;
用来添加约束,并且上面方法的返回值都是MASConstraint
这样我们就可以通过链式编程创建约束们了。这样我们就可以通过[constraintMaker install]来更新约束了。
以上是关于Masonry 源码简单解析的主要内容,如果未能解决你的问题,请参考以下文章