Masonry 源碼解讀(上)

前言

iOS 開發中的佈局方式,整體而言通過了三個時代。混沌初開之時,世間只有3.5英寸(iPhone 四、iPhone 4S),那個時候屏幕適配對於大多數 iOS 開發者來講並非什麼難題,用 frame 就能精確高效的定位。這以後,蘋果發佈了4英寸機型(iPhone 五、iPhone 5C、iPhone 5S),與此同時蘋果也推出了 AutoresizingMask,用來協調子視圖與父視圖之間的關係。再以後,各類各樣的 iPhone 和 iPad 紛紛面世,不只僅是屏幕尺寸方面的差別,更有異形屏(iPhone X)。在此期間,蘋果提出了 AutoLayout 技術,供開發者進行屏幕適配。git

使用 AutoLayout 的方法也有兩種——經過 Interface Builder 或者純代碼。前者一直是蘋果官方文檔裏所鼓勵的,緣由是蘋果從最初到如今,對於 iOS 應用的想法都是小而美的,在他們的認知裏,一個 APP 應該提供儘量小的功能集,這也是爲爲什麼蘋果迄今爲止官方推薦的架構仍然是 MVC,官方推薦的開發方式還是以 StoryBoard(Size Classes)。可是在一些項目較大的公司,StoryBoard 的某些特性(致使應用包過大,減緩啓動速度,合併代碼困難)又是不能爲人所容忍的,便有了純代碼來實現 View 層的一羣開發者(好比我)。github

若是你曾經用代碼來實現 AutoLayout,你會發現蘋果提供的 API 的繁瑣程度使人髮指,這也是 Masonry 這類框架被髮明的緣由。Masonry 是一個輕量級的佈局框架,它使用更好的語法來封裝 AutoLayoutMasonry 有本身的佈局 DSL,它提供了一種鏈式的方式來描述你的 NSLayoutConstraints,從而獲得更簡潔和可讀的佈局代碼。swift

接下來,咱們就從 MasonryREADME 提供的代碼着手,看一看 Masonry 是如何幫助咱們簡化繁瑣的 AutoLayout 代碼的。api

mas_makeConstraints:

舉一個簡單的例子,你想要一個視圖填充它的父視圖,可是在每一邊間隔10個點。數組

UIView *superview = self.view;

UIView *view1 = [[UIView alloc] init];
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
    make.left.equalTo(superview.mas_left).with.offset(padding.left);
    make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
    make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];
複製代碼

咱們想要實現約束的效果,是經過 mas_makeConstraints: 這個方法來實現的,這個方法能夠在任意 UIView 類及其子類上調用,說明其是一個分類方法,這也是這個方法加了 mas_ 前綴的緣由。該方法聲明在 UIView+MASAdditions.h 文件中,先來看一下這個方法的完整聲明:bash

- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
複製代碼

這個方法傳遞的參數是一個參數爲 MASConstraintMaker 類型的無返回值的 block,而該方法的返回值則是一個數組。方法聲明中咱們看到了一個叫作 NS_NOESCAPE 的宏,NS_NOESCAPE 用於修飾方法中的 block 類型參數,做用是告訴編譯器,該 block 在方法返回以前就會執行完畢,而不是被保存起來在以後的某個時候再執行。編譯器被告知後,就會相應的進行一些優化。更詳細的內容請參考 Add @noescape to public library API架構

接下來是方法的實現:app

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    block(constraintMaker);
    return [constraintMaker install];
}
複製代碼

self.translatesAutoresizingMaskIntoConstraints = NO;

默認狀況下,view 上的 autoresizing mask 會產生約束條件,以徹底肯定視圖的位置。這容許 AutoLayout 系統跟蹤其佈局被手動控制的 viewframe(例如經過 setFrame:)。當你選擇經過添加本身的約束來使用 AutoLayout 來定位視圖時,必須 self.translatesAutoresizingMaskIntoConstraints = NO;。IB 會自動爲你作這件事。框架

constraintMaker

在這以後,創造了一個 MASConstraintMaker 類型的對象 constraintMakerMASConstraintMaker 的初始化方法爲:ide

- (id)initWithView:(MAS_VIEW *)view;
複製代碼

能夠看到,傳入的 view 參數是由一個叫作 MAS_VIEW 的宏來做爲參數聲明的,MAS_VIEW 的定義是:

#if TARGET_OS_IPHONE || TARGET_OS_TV

    #import <UIKit/UIKit.h>
    #define MAS_VIEW UIView
    ...

#elif TARGET_OS_MAC

    #import <AppKit/AppKit.h>
    #define MAS_VIEW NSView
    ...

#endif
複製代碼

由於 Masonry 是一個跨平臺的框架,因而經過預編譯宏來讓在不一樣的平臺上,MAS_VIEW 表明的意義不一樣。接下來看初始化方法的實現:

- (id)initWithView:(MAS_VIEW *)view {
    self = [super init];
    if (!self) return nil;
    
    self.view = view;
    self.constraints = NSMutableArray.new;
    
    return self;
}
複製代碼

在初始化方法中,將傳入的 view 經過一個弱指針(@property (nonatomic, weak) MAS_VIEW *view;)保留在了 constraintMaker 中,同時初始化了一個名爲 constraintsNSMutableArray,用來保存約束。

block(constraintMaker);

接着,constraintMaker 經過 block(constraintMaker); 傳遞給了咱們,而咱們對它作了什麼呢?

make.top.equalTo(superview.mas_top).with.offset(padding.top); 
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
複製代碼

咱們將對視圖約束的描述,以一種迥異於建立 NSLayoutConstraints 對象的方式描述了咱們對視圖的約束,咱們以其中的一句做爲例子來看看 Masonry 是如何實現鏈式 DSL 的。

make.top.equalTo(superview.mas_top).with.offset(padding.top);
複製代碼

make.top

makeMASConstraintMaker 類型的對象,這個類型封裝了一系列只讀 MASConstraint 屬性,top 就是其中之一,聲明和實現以下:

@property (nonatomic, strong, readonly) MASConstraint *top;
複製代碼
- (MASConstraint *)top {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}
複製代碼

addConstraintWithLayoutAttribute: 方法的實現爲:

- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}

間接調用了 `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;
    }
    if (!constraint) {
        newConstraint.delegate = self;
        [self.constraints addObject:newConstraint];
    }
    return newConstraint;
}
複製代碼

讓咱們一句一句來看:

MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
複製代碼

首先,會初始化一個 MASViewAttribute 類型的對象(viewAttribute),該類型的初始化方法是:

- (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute;
複製代碼

實現爲:

- (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute {
    self = [self initWithView:view item:view layoutAttribute:layoutAttribute];
    return self;
}

- (id)initWithView:(MAS_VIEW *)view item:(id)item layoutAttribute:(NSLayoutAttribute)layoutAttribute {
    self = [super init];
    if (!self) return nil;
    
    _view = view;
    _item = item;
    _layoutAttribute = layoutAttribute;
    
    return self;
}
複製代碼

MASViewAttribute 是一個模型類,用於存儲視圖和視圖對應的 NSLayoutAttribute

接下來會初始化一個 MASViewConstraint 類型的對象(newConstraint):

MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
複製代碼

MASViewConstraint的初始化方法是:

- (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute;
複製代碼
- (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute {
    self = [super init];
    if (!self) return nil;
    
    _firstViewAttribute = firstViewAttribute;
    self.layoutPriority = MASLayoutPriorityRequired;
    self.layoutMultiplier = 1;
    
    return self;
}
複製代碼

MASViewConstraint 也是一個模型類,會經過剛纔初始化的 viewAttribute 做爲初始化參數,並存儲在 _firstViewAttribute 的實例變量中。

接下來,因爲 constraint 參數爲 nil, 因此直接走到這裏:

if (!constraint) {
    newConstraint.delegate = self;
    [self.constraints addObject:newConstraint];
}
複製代碼

newConstraint 對象的代理設爲 self (make),同時將其放置到 constraints 數組中。

簡而言之就是,make 中的 top 方法會初始化一個 MASViewAttribute 類型的對象 viewAttribute,並經過該對象初始化一個 MASViewConstraint 類型的對象 newConstraint,讓 make 做爲 newConstraint 對象的 delegate,並存儲在 makeconstraints 屬性中。接下來,return newConstraint;

return newConstraint; 看似簡簡單單的一句代碼,倒是 Masonry 這套鏈式 DSL 可以生效的核心。

.equalTo

newConstraintMASViewConstraint 類型的對象,而 MASViewConstraint 又是 MASConstraint 的子類。在 MASConstraint 中,聲明瞭一系列的方法,例如:

/**
 *	Sets the constraint relation to NSLayoutRelationEqual
 *  returns a block which accepts one of the following:
 *    MASViewAttribute, UIView, NSValue, NSArray
 *  see readme for more details.
 */
- (MASConstraint * (^)(id attr))equalTo;
複製代碼

這個方法的返回值是一個接受 id 類型的 attr 參數,返回 MASConstraint 類型的 block,這樣寫的意義是,既能夠作到傳遞參數,返回 self,同時又確保了能夠實現鏈式調用的 DSL

該方法的實現爲:

- (MASConstraint * (^)(id))equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}
複製代碼

在方法的實現中,調用了一個名爲 equalToWithRelation 的內部方法,方法的實現爲:

- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { MASMethodNotImplemented(); }
複製代碼

MASConstraint 實際上是一個基類,其中 equalToWithRelation 方法自己的實現裏只有一個名爲 MASMethodNotImplemented(); 的宏,這個宏的實現僅僅是拋出一個異常:

#define MASMethodNotImplemented() \
    @throw [NSException exceptionWithName:NSInternalInconsistencyException \
                                   reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] \
                                 userInfo:nil]
複製代碼

而咱們在 makeConstraints 的時候,實際調用的是 MASViewConstraint 這個 MASConstraint 子類中的實現:

- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
    return ^id(id attribute, NSLayoutRelation relation) {
        if ([attribute isKindOfClass:NSArray.class]) {
            NSAssert(!self.hasLayoutRelation, @"Redefinition of constraint relation");
            NSMutableArray *children = NSMutableArray.new;
            for (id attr in attribute) {
                MASViewConstraint *viewConstraint = [self copy];
                viewConstraint.layoutRelation = relation;
                viewConstraint.secondViewAttribute = attr;
                [children addObject:viewConstraint];
            }
            MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
            compositeConstraint.delegate = self.delegate;
            [self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint];
            return compositeConstraint;
        } else {
            NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation");
            self.layoutRelation = relation;
            self.secondViewAttribute = attribute;
            return self;
        }
    };
}
複製代碼

該方法接收兩個參數,一個表示了對應的屬性(mas_top),一個表示了相等關係(NSLayoutRelationEqual),進入方法後會先對咱們傳入的屬性作一個類型判斷,咱們傳入的是一個單個的屬性,因此會落入 else 分支,一樣是依賴斷言作了一系列保護性的判斷,並將相等關係和視圖屬性分別賦值給 layoutRelationsecondViewAttribute 屬性,並返回 self

返回 self,看似簡簡單單的一個操做,倒是 Masonry 可以實現鏈式 DSL 最重要的基石。(重要的事情說 n 遍)

superview.mas_top

再來看看咱們傳入的 mas_top,這是一個聲明在 View+MASAdditions.h 當中的只讀屬性:

@property (nonatomic, strong, readonly) MASViewAttribute *mas_top;
複製代碼

簡單生成並返回了一個 MASViewAttribute 屬性的對象:

- (MASViewAttribute *)mas_top {
    return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeTop];
}
複製代碼

實現爲:

- (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute;
複製代碼
- (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute {
    self = [self initWithView:view item:view layoutAttribute:layoutAttribute];
    return self;
}

- (id)initWithView:(MAS_VIEW *)view item:(id)item layoutAttribute:(NSLayoutAttribute)layoutAttribute {
    self = [super init];
    if (!self) return nil;
    
    _view = view;
    _item = item;
    _layoutAttribute = layoutAttribute;
    
    return self;
}
複製代碼

.with

咱們繼續往下看的時候,.with 一樣是實如今 MASViewConstraint 中的一個方法:

- (MASConstraint *)with {
    return self;
}
複製代碼

一樣是簡簡單單的返回了 self,並且是僅僅作了這一件事情。因此這個方法僅僅是一個語法……好吧都不能叫作語法糖,就叫語氣助詞吧,是爲了讓咱們寫出的 DSL 可讀性更高而存在的。固然了,你要是以爲多餘,也是能夠不寫的。

.offset

接下來是 offset

- (MASConstraint * (^)(CGFloat offset))offset;
複製代碼

就是簡簡單單的一個賦值操做罷了,寫成這麼複雜的緣由就是實現能夠傳遞參數的鏈式調用。

- (MASConstraint * (^)(CGFloat))offset {
    return ^id(CGFloat offset){
        self.offset = offset;
        return self;
    };
}
複製代碼

以上就是 make.top.equalTo(superview.mas_top).with.offset(padding.top);Masonry 內部到底作了些什麼,其他幾句也是相似的,總而言之就是:

MASConstraintMaker 類型的對象屬性(MASConstraint的子類) top(或其餘任何你想要去佈局的屬性),進行了初始化,並經過返回 MASViewConstraint 類型的對象(newConstraint),不斷地調用 newConstraint 的對象方法,對 newConstraint 中的屬性作了賦值,以確保其能夠完整的表達一個 NSLayoutConstraints

install

在配置好咱們想要的約束後,咱們還須要對視圖施加約束:

return [constraintMaker install];
複製代碼

先來看一下 install 方法:

- (NSArray *)install;
複製代碼
- (NSArray *)install {
    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) {
        constraint.updateExisting = self.updateExisting;
        [constraint install];
    }
    [self.constraints removeAllObjects];
    return constraints;
}
複製代碼

首先,會對 constraints 屬性作一份 copy,以後遍歷 constraints 中的全部 MASConstraint 及其子類型的屬性,並調用其 install 方法:

- (void)install;
複製代碼

實現爲:

- (void)install {
    if (self.hasBeenInstalled) {
        return;
    }
    
    if ([self supportsActiveProperty] && self.layoutConstraint) {
        self.layoutConstraint.active = YES;
        [self.firstViewAttribute.view.mas_installedConstraints addObject:self];
        return;
    }
    
    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)
    if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
        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];
    
    layoutConstraint.priority = self.layoutPriority;
    layoutConstraint.mas_key = self.mas_key;
    
    if (self.secondViewAttribute.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);
        self.installedView = closestCommonSuperview;
    } else if (self.firstViewAttribute.isSizeAttribute) {
        self.installedView = self.firstViewAttribute.view;
    } else {
        self.installedView = self.firstViewAttribute.view.superview;
    }


    MASLayoutConstraint *existingConstraint = nil;
    if (self.updateExisting) {
        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;
        [firstLayoutItem.mas_installedConstraints addObject:self];
    }
}
複製代碼

接下來,咱們來一段一段分析:

hasBeenInstalled

首先,在 install 以前,會作一次判斷,看是否已經被 install 過:

if (self.hasBeenInstalled) {
    return;
}
複製代碼

判斷依據則是:

- (BOOL)hasBeenInstalled {
    return (self.layoutConstraint != nil) && [self isActive];
}
複製代碼

layoutConstraint 是一個 MASLayoutConstraint 類型的 weak 屬性,MASLayoutConstraintNSLayoutConstraint 的子類,只是爲了增長一個屬性(mas_key)。

@property (nonatomic, weak) MASLayoutConstraint *layoutConstraint;
複製代碼

isActive 則是經過判斷 layoutConstraint 是否響應 isActive 以及 isActive 方法返回的結果,來綜合決定。

- (BOOL)isActive {
    BOOL active = YES;
    if ([self supportsActiveProperty]) {
        active = [self.layoutConstraint isActive];
    }

    return active;
}
複製代碼
- (BOOL)supportsActiveProperty {
    return [self.layoutConstraint respondsToSelector:@selector(isActive)];
}
複製代碼

mas_installedConstraints

if ([self supportsActiveProperty] && self.layoutConstraint) {
    self.layoutConstraint.active = YES;
    [self.firstViewAttribute.view.mas_installedConstraints addObject:self];
    return;
}
複製代碼

若是 supportsActivePropertylayoutConstraint 不爲空,則將 layoutConstraint.active 設爲 YES,並將其添加到 firstViewAttribute.viewmas_installedConstraints 只讀屬性中去。

@property (nonatomic, readonly) NSMutableSet *mas_installedConstraints;
複製代碼

mas_installedConstraints 是一個可變集合,是經過分類給 MAS_View 類添加的關聯對象,用來保存已經 active 的對象。

static char kInstalledConstraintsKey;

- (NSMutableSet *)mas_installedConstraints {
    NSMutableSet *constraints = objc_getAssociatedObject(self, &kInstalledConstraintsKey);
    if (!constraints) {
        constraints = [NSMutableSet set];
        objc_setAssociatedObject(self, &kInstalledConstraintsKey, constraints, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return constraints;
}
複製代碼

生成約束

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)
if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
    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];
    
layoutConstraint.priority = self.layoutPriority;
layoutConstraint.mas_key = self.mas_key;
複製代碼

其實就是把咱們再 block 中經過諸如 make.top.equalTo(superview.mas_top).with.offset(padding.top); 這樣的語句配置的屬性做爲 MASLayoutConstraint 初始化方法的參數,生成一個約束。惟一須要注意的是,若是你設置的是一個 isSizeAttribute,而且 secondViewAttributenil,會作一些額外的參數調整。

施加約束

if (self.secondViewAttribute.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);
    self.installedView = closestCommonSuperview;
} else if (self.firstViewAttribute.isSizeAttribute) {
    self.installedView = self.firstViewAttribute.view;
} else {
    self.installedView = self.firstViewAttribute.view.superview;
}
複製代碼

若是 secondViewAttribute.view 中的存在,就經過 mas_closestCommonSuperview 方法尋找最近的公共子視圖:

/**
 *	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;
}
複製代碼

遞歸求解。

若是設置的是一個尺寸約束(firstViewAttribute.isSizeAttribute),則施加在 firstViewAttribute.view 上。不然施加在 firstViewAttribute.view.superView 上。

MASLayoutConstraint *existingConstraint = nil;
if (self.updateExisting) {
    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;
    [firstLayoutItem.mas_installedConstraints addObject:self];
}
複製代碼

對視圖施加約束,並將 layoutConstraint 存儲在 self.layoutConstraint 屬性中,同時把 self 存儲到以前提到過的叫作 mas_installedConstraints 的關聯對象中。至此,文章開始提到的例子業已完成。

原文地址:Masonry 源碼解讀(上)

若是以爲我寫的還不錯,請關注個人微博@小橘爺,最新文章即時推送~

相關文章
相關標籤/搜索