Masonry 源碼解析

上圖是 Masonry 的大體結構。
Masonry 主要分兩層,一部分通過主要的用戶接口及通過 DSL 能力。另一部分則是核心功能代碼。
DSL
Masonry 通過自定義 DSL,讓用戶可以通過簡單的代碼實現(xiàn)布局。
mas_makeConstraints 是我們最常用的方法。
mas_makeConstraints
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
// 構建一個 MASConstraintMaker 對象
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
// 將對象傳給 block
block(constraintMaker);
// 執(zhí)行 install 布局
return [constraintMaker install];
}
mas_makeConstraints 在,給 block 傳入 MASConstraintMaker 對象。
left
MASConstraintMaker 和 MASConstraint 中定義了大量的屬性。

- (MASConstraint *)left {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
這些屬性的代碼,大概都長這樣。添加完實現(xiàn)后,又返回 MASConstraint 。這樣使得我們能夠通過 . 語法不斷的添加 Layout 屬性。
addConstraintWithLayoutAttribute:
MASConstraintMaker 中的 addConstraintWithLayoutAttribute: 最終調(diào)用 constraint:addConstraintWithLayoutAttribute:
- (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;
}
// 對于 View 的依賴處理
if (!constraint) {
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];
}
return newConstraint;
}
MASCompositeConstraint和MASViewConstraint都是MASConstraint的子類。- 方法會返回
MASCompositeConstraint或MASViewConstraintconstraint:addConstraintWithLayoutAttribute:是delegate的方法。MASConstraintMaker的方法中將newConstraint.delegate = self;,compositeConstraint.delegate = self;。使用在調(diào)用MASConstraint的left這樣的屬性時,最終還是會回到MASConstraintMaker的這個方法中。constraint:addConstraintWithLayoutAttribute:主要做了一件事,就是把MASCompositeConstraint或MASViewConstraint對象存入數(shù)組中。等mas_makeConstraints進行install。
offset
MASConstraint 還存在一類屬性,是允許傳入?yún)?shù)的。 offset 就是最典型的例子。
- (MASConstraint * (^)(CGFloat))offset {
return ^id(CGFloat offset){
self.offset = offset;
return self;
};
}
所以,這類屬性時通過 block 實現(xiàn)。設置完屬性后返回 self 。這樣使得鏈式調(diào)用可以繼續(xù)。
宏
equalTo & mas_equalTo
Masonry 定義了兩個預編譯宏。一個 MAS_SHORTHAND 用于控制可以使用 makeConstraints 方法調(diào)用。不需要 mas_ 作為前綴。
另一個是 MAS_SHORTHAND_GLOBALS
#define mas_equalTo(...) equalTo(MASBoxValue((__VA_ARGS__)))
#define mas_greaterThanOrEqualTo(...) greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_lessThanOrEqualTo(...) lessThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_offset(...) valueOffset(MASBoxValue((__VA_ARGS__)))
#ifdef MAS_SHORTHAND_GLOBALS
#define equalTo(...) mas_equalTo(__VA_ARGS__)
#define greaterThanOrEqualTo(...) mas_greaterThanOrEqualTo(__VA_ARGS__)
#define lessThanOrEqualTo(...) mas_lessThanOrEqualTo(__VA_ARGS__)
#define offset(...) mas_offset(__VA_ARGS__)
#endif
equalTo 宏
如果 MAS_SHORTHAND_GLOBALS 定義了則 equalTo 等同與調(diào)用 equalTo(MASBoxValue((__VA_ARGS__)))
調(diào)用
equalTo宏 -> 調(diào)用mas_equalTo宏 -> 調(diào)用equalTo(MASBoxValue((__VA_ARGS__)))
但如果 MAS_SHORTHAND_GLOBALS 沒定義調(diào)用的是 MASConstraint 的 equalTo 屬性。
MASBoxValue
#define MASBoxValue(value) _MASBoxValue(@encode(__typeof__((value))), (value))
而 MASBoxValue 最終調(diào)用了 _MASBoxValue 。做了一件事,就是把 int , double 等轉(zhuǎn)換為 NSNumber ; 把 CGPoint , CGSize 轉(zhuǎn)換為 NSValue 。
為什么要做一層轉(zhuǎn)換呢?看了 equalTo 屬性的定義我們就明白了。
equalTo 屬性
- (MASConstraint * (^)(id attr))equalTo;
equalTo 屬性的定義, 是個 block , 返回 MASConstraint , 接收一個 id 類型的入?yún)ⅰ?而 int , double , CGPoint 這些并非對象, 所以 需要通過 MASBoxValue 做個轉(zhuǎn)換。
總結
- Masonry 的核心代碼主要在
MASConstraintMaker、MASViewConstraint、MASCompositeConstraint。 - Masonry 主要通過屬性和 block 的方式實現(xiàn)鏈式調(diào)用,自定義了一套 DSL 。
- Masonry 定義了些宏,這些宏會改變
equalTo這樣的一些操作的行為。在使用是需要特別注意 。