淺析iOS-MAS&鏈式程式設計思想

窗前有月光發表於2018-11-20

前言

程式設計思想在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時所建立的constraintsconstraints裡面實際上儲存的是我們為控制元件新增的所有約束資訊,然後分別對每條約束之行install。那麼問題來了,constraints裡面的約束從哪裡來的呢。這就要回到了上面說的第2步,block將約束製造者返回給使用者,讓使用者通過constraintMaker去設定控制元件的約束,這些約束實際上就是儲存到了constraints中。當執行第3return [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...
}];
複製程式碼

這下應該就很好理解了。

最後總結一下什麼是鏈式程式設計,一句話就是方法返回值必須要有方法的呼叫者!

來源:https://juejin.im/post/5bf3f803e51d45228a1824a2

相關文章