淺析iOS-MAS&鏈式程式設計思想
程式設計思想在iOS
的應用中大概有那麼幾類,我們最常用的當屬於面向物件的程式設計思想,一切皆物件,基於這種思想離不開的就是我們最常用的封裝、繼承、多型。平時工作中我們也會接觸一些面向協議的程式設計思想,比如說介面分離解耦合,再比如說我們最常用的delegate
都是面向協議的思想,還有就是基於ReactiveCocoa
框架也就是平時聽到的RAC提供的響應式程式設計思想,今天主要分析下另一種程式設計思想,鏈式程式設計。
首先簡單分析一下Masonry的實現過程:
Masonry
框架作為iOS
開發者耳熟能詳,大家在做純程式碼適配的時候應該都曾用過,下面就以Masonry
為例,但本文不過多的分析MAS
原始碼,旨在提煉思想。
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block { self.translatesAutoresizingMaskIntoConstraints = NO; MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; block(constraintMaker); return [constraintMaker install]; } - (id)initWithView:(MAS_VIEW *)view { self = [super init]; if (!self) return nil; self.view = view; self.constraints = NSMutableArray.new; return self; } 複製程式碼
-
1.建立約束製造者
MASConstraintMaker
並且繫結控制元件,在約束製造者init
的同時生成了一個儲存所有約束的陣列constraints
。 -
2.執行
mas_makeConstraints
傳入的block
,返回我們剛才建立的約束製造者constraintMaker
。 -
3.讓約束製造者安裝約束,執行
install
方法。
我們再看一下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; } 複製程式碼
實際上是遍歷了我們建立約束製造者constraintMaker
時所建立的constraints
,constraints
裡面實際上儲存的是我們為控制元件新增的所有約束資訊,然後分別對每條約束之行install
。那麼問題來了,constraints
裡面的約束從哪裡來的呢。這就要回到了上面說的第2
步,block
將約束製造者返回給使用者,讓使用者通過constraintMaker
去設定控制元件的約束,這些約束實際上就是儲存到了constraints
中。當執行第3
步return [constraintMaker install];
的時候,就是將所有呼叫者新增的佈局轉換為NSLayoutConstraint
物件也就是我們熟悉的純程式碼適配,進行佈局更新。
為什麼要先研究Masonry呢
實際上Masonry
就是基於鏈式程式設計思想實現的開源框架,即強大,又直觀。比如說我們在使用Masonry
的時候通常會這樣寫:
[view mas_makeConstraints:^(MASConstraintMaker *make) { make.height.top.mas_equalTo(44); make.left.mas_equalTo(5); make.centerY.equalTo(self); }]; 複製程式碼
為什麼make
可以一直這麼點下去,點語法給我們的第一感覺是get
方法,實際上確實是get
方法:
- (MASConstraint *)height { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeHeight]; } 複製程式碼
通過原始碼可以看到.height
實際上返回的並不是一個int
或者NSInteger
型別變數,而是MASConstraint
,是約束製造者本身,實際上每次呼叫.height就是將約束新增到我們上面提到的constraints陣列中。到這裡,鏈式程式設計的特點就顯而易見了,那就是方法返回值必須要有方法的呼叫者
。那麼還有疑問,mas_equalTo()
是什麼鬼。下面著重講一下.mas_equalTo()
,
還是通過原始碼點進去看一下:
- (MASConstraint * (^)(id))equalTo { return ^id(id attribute) { return self.equalToWithRelation(attribute, NSLayoutRelationEqual); }; } 複製程式碼
返回值是一個MASConstraint * (^)(id)
的block
型別,到這裡我們瞭解了.mas_equalTo
實際上是返回了一個返回值為MASConstraint
型別的block
,當我們外面呼叫block
的時候實際上執行的是self.equalToWithRelation(attribute,NSLayoutRelationEqual);
函式,沒錯,它返回的依舊是MASConstraint
型別,依舊是方法的呼叫者,也就是前面提到的約束製造者,所以我們在呼叫mas_equalTo()
之後還能繼續點下去,像不像個無底洞。如果這樣不好理解的話我們可以將新增約束的原始碼改寫一下來實現:
[view mas_makeConstraints:^(MASConstraintMaker *make) { //make.height.top.mas_equalTo(44); MASConstraint * (^)(id)block = make.height.top.mas_equlTo; MASConstraint *make = block(44); make.top... }]; 複製程式碼
這下應該就很好理解了。
最後總結一下什麼是鏈式程式設計,一句話就是方法返回值必須要有方法的呼叫者!