iOS-block的妙用:鏈式程式設計(經典應用:Masonry)
iOS開發當中有一個很特殊的存在,這個特殊就是block。在OC當中實現某一個功能都是一個 響應物件 呼叫一個 響應方法 ,簡而言之就是Target-Action。
以UIButton為例:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
UIButton的事件就是一個響應物件和一個響應方法之間的一一對映。
與UIButton這種不同,block則不一樣,其根本不需要響應物件,如下:
void (^LogString)(NSString *string) = ^(NSString *string) { NSLog(@"這是一個用來列印log的block,需要列印的字串為:%@", string); }; LogString(@"12345");
block的使用無非是以下三個步驟:
1. block的宣告 。上述程式碼中的 void (^LogString)(NSString *string
就是將 void (^)(NSString *string)
宣告成一個叫 LogString
的block
2. block的實現 。上述程式碼中'='後面的便是block的實現,包含了引數與返回值
3. block的呼叫 。上述程式碼中的 LogString(@"12345")
就是對block的呼叫
今天小編就來說說block的另外一項應用: 鏈式程式設計 。
說到鏈式程式設計最經典的應用便是Masonry了,以下便是Masonry的簡單程式碼:
[self.nicknameLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.top.equalTo(view).priorityHigh(); }];
其中可以看到對於make這個MASConstraintMaker物件的設定是以 一系列的點語法來實現的 。
當然,這個 leading 和 top 也是很容易實現的,無非就是以系列的 get 方法而已。但是後面的 equalTo(view) 和 priorityHigh() 是什麼鬼?我們點進去:

Masonry原始碼
這個時候可以看到 equalTo方法的返回值是一個block,而equalTo裡面則是一個block的宣告,並且將這個block給return了 。
如此我們在呼叫 equalTo(view) 的時候經歷了以下步驟:
1. 呼叫equalTo方法的物件是MASConstraint,equalTo方法裡面聲明瞭一個block和block的實現
2. MASConstraint物件呼叫equalTo方法實際上是返回了一個block,這個block的返回值還是MASConstraint物件
3. 步驟2獲取到一個block,這個block引數是一個id型別的資料,那麼equalTo後面的view引數實際上就作為block的引數了,這也就實現了一個block的呼叫
4. 因為block的返回值是MASConstraint物件,所以經歷步驟3之後的值實際上還是MASConstraint,呼叫priorityHigh()就又回到了步驟1
說來說去是不是繞糊塗了?實際上這無非就是 block的宣告,block的實現與block的實現 這三個步驟的另外一種變體。
接下來我們依據上面總結的Masonry的步驟來對UILabel實現鏈式程式設計:
1. 宣告一個UILabel的分類 ,如下:

UILabel的分類
2. 初始化UILabel的方法宣告 。在 UILabel+Additions.h
裡宣告一個叫label的方法,這個方法沒有引數,返回值是一個block,這個block的返回值是一個UILabel物件,block的引數是CGRect的結構體。
/** * 初始化label,此方法是類方法且沒有引數 * 此方法的返回值是一個block * 這個block引數為一個CGRect的引數 * 返回值還是UILabel物件,用來實現鏈式程式設計 */ + (UILabel *(^)(CGRect frame))label;
3. 初始化UILabel的方法實現 。在 UILabel+Additions.m
實現 label 方法,最主要是實現block,這個在block裡實現UILabel的建立,並將這個block返回,如下:
+ (UILabel * _Nonnull (^)(CGRect))label { // 宣告並實現一個block,並返回 return ^(CGRect frame) { // 根據傳過來的frame引數初始化UILabel,並返回 return [[UILabel alloc] initWithFrame:frame]; }; }
同理,設定UILabel的text和textColor也是如此,以下便是程式碼:
在 UILabel+Additions.h
進行方法的宣告:
// 物件方法 - (UILabel *(^)(NSString *title))title; - (UILabel *(^)(UIColor *titleColor))titleColor;
在 UILabel+Additions.m
對方法進行實現:
- (UILabel * _Nonnull (^)(NSString * _Nonnull))title { return ^(NSString *title) { self.text = title; // 返回self就可以了 return self; }; } - (UILabel * _Nonnull (^)(UIColor * _Nonnull))titleColor { return ^(UIColor *titleColor) { self.textColor = titleColor; // 返回self就可以了 return self; }; }
應用程式碼:
UILabel *label = UILabel.label(CGRectMake(100, 100, 200, 50)). title(@"鏈式程式設計的應用"). titleColor([UIColor greenColor]); label.backgroundColor = [UIColor redColor]; [self.view addSubview:label];
效果:

效果圖