iOS開發(fā)Masonry框架源碼解析

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)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市女仰,隨后出現(xiàn)的幾起案子猜年,更是在濱河造成了極大的恐慌抡锈,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乔外,死亡現(xiàn)場離奇詭異床三,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)杨幼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門撇簿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人差购,你說我怎么就攤上這事四瘫。” “怎么了歹撒?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵莲组,是天一觀的道長。 經(jīng)常有香客問我暖夭,道長锹杈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任迈着,我火速辦了婚禮竭望,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘裕菠。我一直安慰自己咬清,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布奴潘。 她就那樣靜靜地躺著旧烧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪画髓。 梳的紋絲不亂的頭發(fā)上掘剪,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音奈虾,去河邊找鬼夺谁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛肉微,可吹牛的內(nèi)容都是我干的匾鸥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼碉纳,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼勿负!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起劳曹,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤笆环,失蹤者是張志新(化名)和其女友劉穎攒至,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躁劣,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡迫吐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了账忘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片志膀。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鳖擒,靈堂內(nèi)的尸體忽然破棺而出溉浙,到底是詐尸還是另有隱情,我是刑警寧澤蒋荚,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布戳稽,位于F島的核電站,受9級(jí)特大地震影響期升,放射性物質(zhì)發(fā)生泄漏惊奇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一播赁、第九天 我趴在偏房一處隱蔽的房頂上張望颂郎。 院中可真熱鬧,春花似錦容为、人聲如沸乓序。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽替劈。三九已至,卻和暖如春得滤,著一層夾襖步出監(jiān)牢的瞬間陨献,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來泰國打工耿戚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留湿故,地道東北人阿趁。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓膜蛔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親脖阵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子皂股,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容