iOS開發(fā)Masonry框架源碼解析
前言:這個(gè)框架編程思想主要包括鏈?zhǔn)骄幊?/p>
這是一個(gè)iOS在控件布局中的輕量級(jí)框架搁嗓,簡化了NSLayoutConstraint的使用方式,讓我們用鏈?zhǔn)阶兂傻乃枷脒M(jìn)行對(duì)View控件的約束啄骇。
本篇主要圍繞Masonry框架源碼進(jìn)行解析丹诀,從而透析Masonry是如何對(duì)NSLayoutConstraint進(jìn)行封裝的逆巍。
####1.OC中的鏈?zhǔn)骄幊淌侨绾螌?shí)現(xiàn)的?
鏈?zhǔn)骄幊痰暮锰帲?/p>
點(diǎn)語法+事物+串聯(lián)
1.簡化代碼
2.代碼可讀性高
調(diào)用:
self.where.select;
~~~
- (ViewController*)where{
return self;
}
- (void)select{
}
~~~
用getter+有參數(shù)的時(shí)候應(yīng)該如何寫呢封锉?
~~~
- (ViewController*)where{
return self;
}
//block有保存代碼塊的特性
- (void(^)(NSString*))select{
void(^block)(NSString *word) = ^(NSString *str){
};
}
~~~
舉個(gè)栗子:self.where.select(@"參數(shù)")绵跷。
#####2.框架解析正文
Masonry框架和NSLayoutConstraint調(diào)用的對(duì)比:
###實(shí)際應(yīng)用:
![avatar](images/Masonry類結(jié)構(gòu)圖.png)
#####1.對(duì)誰做約束
View+MASAdditions 就是 Masonry 的一個(gè)外部的入口膘螟,實(shí)質(zhì)上就是 UIView 的一個(gè) Category 作用就是用來設(shè)置 MASViewAttribute 的屬性,并實(shí)例化碾局,并且指定當(dāng)前的 UIView 對(duì)應(yīng)的約束(LayoutAttribute)
MASUtilities.h 主要是這里除了平臺(tái)相關(guān)代碼外荆残,還有些宏的定義和靜態(tài)方法。Masonry 也對(duì) iOS 和 macOS 做了兼容净当,在 macOS 里就是 NSView内斯。
其中靜態(tài)方法
```
static inline id _MASBoxValue(const char *type, ...)
```
是我們經(jīng)常使用的 mas_equalTo 這個(gè)方法,這里可以看到它是如何支持變參和如何將 float像啼,double俘闯,int 這樣的值類型數(shù)據(jù)轉(zhuǎn)換成和 equalTo 一樣的對(duì)象 NSNumber 數(shù)據(jù)的。這個(gè)寫法靈感來自GitHub [- specta/expecta: A Matcher Framework for Objective-C/Cocoa ](https://github.com/specta/expecta)忽冻。 mas_equalTo 和 equalTo 都是宏定義的真朗。
```
#define mas_equalTo(...)? ? ? ? ? ? ? ? equalTo(MASBoxValue((__VA_ARGS__)))
#define MASBoxValue(value) _MASBoxValue(@encode(__typeof__((value))), (value))
#define equalTo(...)? ? ? ? ? ? ? ? ? ? mas_equalTo(__VA_ARGS__)
```
方法實(shí)現(xiàn):
```
- (MASConstraint * (^)(id))equalTo {
? ? return ^id(id attribute) {
? ? ? ? return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
? ? };
}
- (MASConstraint * (^)(id))mas_equalTo {
? ? return ^id(id attribute) {
? ? ? ? return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
? ? };
}
```
宏定義不同但是實(shí)現(xiàn)方法相同這樣寫就是避免宏定義沖突的一種方式。
mas_makeConstraints 的的 block 參數(shù)會(huì)將創(chuàng)建的 MASConstraintMaker 這個(gè)工廠類對(duì)象暴露出去僧诚,讓我們?nèi)ピO(shè)置這個(gè)類對(duì)象中的 MASConstraint 屬性遮婶,然后通過該對(duì)象的 install 方法將當(dāng)前視圖中所有添加的約束添加到一個(gè)數(shù)組里。該數(shù)組里存儲(chǔ)是 MASViewConstraint 對(duì)象湖笨,對(duì)應(yīng)的就是 NSLayoutConstraint旗扑。
```
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block;
```
```
//創(chuàng)建約束
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
? ? //去掉自動(dòng)autoresizing轉(zhuǎn)為約束的
? ? self.translatesAutoresizingMaskIntoConstraints = NO;
? ? //構(gòu)建builder
? ? MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
? ? //運(yùn)行builder
? ? block(constraintMaker);
? ? //附值約束返回
? ? return [constraintMaker install];
}
```
#####2.如何做約束
Masonry通過MASConstraintMaker來創(chuàng)建MASConstraint對(duì)象。里面有個(gè) constraints 數(shù)組專門用來存儲(chǔ)創(chuàng)建的這些對(duì)象慈省。前面 mas_makeConstraints 的那個(gè) Block 暴露出的就是 MASConstraintMaker 對(duì)象臀防。
```
//property 重寫了getter方法,使得每次鏈?zhǔn)秸{(diào)用相當(dāng)于構(gòu)造一個(gè)約束
@property (nonatomic, strong, readonly) MASConstraint *left;
```
```
//重寫getter方法辫呻,調(diào)用.方法相當(dāng)于構(gòu)造約束
- (MASConstraint *)left {
? ? return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
```
```
//通過NSLayoutAttribute添加約束
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
? ? //構(gòu)造view的MASViewAttribute
? ? MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
? ? //通過MASViewAttribute構(gòu)造第一個(gè)MASViewConstraint
? ? MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
? ? //如果存在contraint,則把constraint和newConstraint組合成MASCompositeConstraint
? ? if ([constraint isKindOfClass:MASViewConstraint.class]) {
? ? ? ? //replace with composite constraint
? ? ? ? NSArray *children = @[constraint, newConstraint];
? ? ? ? MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
? ? ? ? compositeConstraint.delegate = self;
? ? ? ? //替換原來的constraint成新的MASCompositeConstraint
? ? ? ? [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
? ? ? ? return compositeConstraint;
? ? }
? ? //不存在則設(shè)置constraint到self.constraints
? ? if (!constraint) {
? ? ? ? newConstraint.delegate = self;
? ? ? ? [self.constraints addObject:newConstraint];
? ? }
? ? return newConstraint;
}
```
這里會(huì)發(fā)現(xiàn)每次 getter 都會(huì)創(chuàng)建一個(gè)新的 MASViewConstraint 對(duì)象琼锋,這里通過將新的 MASViewConstraint 對(duì)象的 delegate 設(shè)置成自己的方式讓新對(duì)象也能夠調(diào)用相同的方法創(chuàng)建一個(gè)新的 MASViewConstraint 對(duì)象放闺,使得能夠支持進(jìn)行鏈?zhǔn)降恼{(diào)用。
#####約束收集完成之后完成之后的處理
MASViewConstraint這個(gè)類是對(duì) NSLayoutConstriant 的封裝缕坎。它的父類是 MASConstraint怖侦,MASConstraint 是一個(gè)抽象不可實(shí)例的類,里面有接口和協(xié)議谜叹。它的兄弟類是 MASCompositeConstraint匾寝,里面有個(gè)數(shù)組專門存儲(chǔ) MASViewConstraint 對(duì)象。
```
//附值約束返回
? ? return [constraintMaker install];
```
```
- (NSArray *)install {
? ? //如果需要?jiǎng)h除原來的約束
? ? if (self.removeExisting) {
? ? ? ? //獲得所有約束并刪除
? ? ? ? NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
? ? ? ? for (MASConstraint *constraint in installedConstraints) {
? ? ? ? ? ? [constraint uninstall];
? ? ? ? }
? ? }
? ? NSArray *constraints = self.constraints.copy;
? ? for (MASConstraint *constraint in constraints) {
? ? ? ? //設(shè)置更新key
? ? ? ? constraint.updateExisting = self.updateExisting;
? ? ? ? [constraint install];
? ? }
? ? //去除所有緩存的約束結(jié)構(gòu)體
? ? [self.constraints removeAllObjects];
? ? return constraints;
}
```
```
- (void)install {
? ? //已經(jīng)有約束
? ? if (self.hasBeenInstalled) {
? ? ? ? return;
? ? }
? ? //支持active且已經(jīng)有了約束
? ? if ([self supportsActiveProperty] && self.layoutConstraint) {
? ? ? ? //激活約束
? ? ? ? self.layoutConstraint.active = YES;
? ? ? ? //添加約束緩存
? ? ? ? [self.firstViewAttribute.view.mas_installedConstraints addObject:self];
? ? ? ? return;
? ? }
? ? //獲得item1,attribute1,item2,attribute2
? ? MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;
? ? NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;
? ? MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;
? ? NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;
? ? // alignment attributes must have a secondViewAttribute
? ? // therefore we assume that is refering to superview
? ? // eg make.left.equalTo(@10)
? ? //如果attribute是sizeattribute并且沒有第二個(gè)attribute
? ? if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
? ? ? ? //默認(rèn)設(shè)置item2為superview
? ? ? ? secondLayoutItem = self.firstViewAttribute.view.superview;
? ? ? ? secondLayoutAttribute = firstLayoutAttribute;
? ? }
? ? //生成約束
? ? MASLayoutConstraint *layoutConstraint
? ? ? ? = [MASLayoutConstraint constraintWithItem:firstLayoutItem
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? attribute:firstLayoutAttribute
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? relatedBy:self.layoutRelation
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? toItem:secondLayoutItem
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? attribute:secondLayoutAttribute
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? multiplier:self.layoutMultiplier
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? constant:self.layoutConstant];
? ? //設(shè)置priority和mas_key
? ? layoutConstraint.priority = self.layoutPriority;
? ? layoutConstraint.mas_key = self.mas_key;
? ? //如果第二個(gè)attribute有view對(duì)象
? ? if (self.secondViewAttribute.view) {
? ? ? ? //則獲取兩個(gè)view的最小公共view
? ? ? ? MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view];
? ? ? ? NSAssert(closestCommonSuperview,
? ? ? ? ? ? ? ? @"couldn't find a common superview for %@ and %@",
? ? ? ? ? ? ? ? self.firstViewAttribute.view, self.secondViewAttribute.view);
? ? ? ? //設(shè)置約束view為此view
? ? ? ? self.installedView = closestCommonSuperview;
? ? } else if (self.firstViewAttribute.isSizeAttribute) {
? ? ? ? //如果是size attribute則為本身
? ? ? ? self.installedView = self.firstViewAttribute.view;
? ? } else {
? ? ? ? //其它則是superview
? ? ? ? self.installedView = self.firstViewAttribute.view.superview;
? ? }
? ? //已經(jīng)存在的約束
? ? MASLayoutConstraint *existingConstraint = nil;
? ? //需要更新
? ? if (self.updateExisting) {
? ? ? ? //則獲得此生成的約束荷腊,返回和installedview的約束是同類的約束
? ? ? ? existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
? ? }
? ? //如果存在則替換約束
? ? if (existingConstraint) {
? ? ? ? // just update the constant
? ? ? ? existingConstraint.constant = layoutConstraint.constant;
? ? ? ? //緩存約束類型
? ? ? ? self.layoutConstraint = existingConstraint;
? ? } else {
? ? ? ? //其它情況則是直接添加約束
? ? ? ? [self.installedView addConstraint:layoutConstraint];
? ? ? ? //緩存約束類型
? ? ? ? self.layoutConstraint = layoutConstraint;
? ? ? ? //緩存已經(jīng)安裝約束
? ? ? ? [firstLayoutItem.mas_installedConstraints addObject:self];
? ? }
}
```
總結(jié):
###參考資料
1.[Apple-NSLayoutConstraint](https://developer.apple.com/reference/uikit/nslayoutconstraint)
2.[Auto Layout Guide](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/index.html)
3.[Visual Format Language](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html)
3.[深入剖析Auto Layout艳悔,分析iOS各版本新增特性](http://www.starming.com/index.php?v=index&view=84&utm_source=tuicool&utm_medium=referral)
4.[iOS開發(fā)之Masonry框架源碼深度解析](http://www.cnblogs.com/ludashi/p/5591572.html)
5.[Masonry源代碼分析](http://blog.csdn.net/colorapp/article/details/45030163)
6.[史上比較用心的純代碼實(shí)現(xiàn) AutoLayout](http://ios.jobbole.com/85829/)
7.[iOS開發(fā)之Masonry框架源碼解析](https://www.cnblogs.com/ludashi/p/5591572.html)