說(shuō)在前面
項(xiàng)目中用了一個(gè)第三方,其中它使用了鏈?zhǔn)骄幊?。為了封裝它,我照葫蘆畫瓢,寫了幾個(gè)分類方法。但是其中有點(diǎn)技術(shù)點(diǎn)不是很清楚,在這里簡(jiǎn)單記錄下。
本篇博客討論以下幾點(diǎn):
- 1.原則:鏈?zhǔn)骄幊谭祷刂凳且粋€(gè)
Block,Block的返回值是類的實(shí)例對(duì)象 - 2.
maker.sub(123).add(25)的調(diào)用過(guò)程是怎樣的 - 3.為什么單獨(dú)寫
maker.sub(123)不會(huì)警告,而直接寫maker.count會(huì)警告 - 4.更優(yōu)的鏈?zhǔn)骄幊虒懛?/li>
- 5.鏈?zhǔn)骄幊虒?shí)際應(yīng)用
1.原則:鏈?zhǔn)骄幊谭祷刂凳且粋€(gè)Block,Block的返回值是類的實(shí)例對(duì)象
先來(lái)個(gè)加減乘除的Demo
//.h文件
#import <Foundation/Foundation.h>
@interface CPMaker : NSObject
@property (nonatomic, assign) double result;
- (CPMaker *(^)(double))add;
- (CPMaker *(^)(double))sub;
- (CPMaker *(^)(double))multi;
- (CPMaker *(^)(double))divide;
- (CPMaker *)test;
- (NSInteger)count;
@end
//.m文件
#import "CPMaker.h"
@implementation CPMaker
- (CPMaker *(^)(double))add
{
return ^(double num) {
self.result += num;
return self;
};
}
- (CPMaker *(^)(double))sub
{
return ^(double num) {
self.result -= num;
return self;
};
}
- (CPMaker *(^)(double))multi
{
return ^(double num) {
self.result *= num;
return self;
};
}
- (CPMaker *(^)(double))divide
{
return ^(double num) {
if (num == 0.0) {
NSAssert(num != 0.0, @"divide num should not equal zero");
return self;
}
self.result /= num;
return self;
};
}
- (CPMaker *)test
{
NSLog(@"%s",__func__);
return self;
}
- (NSInteger)count
{
return 5;
}
@end
//實(shí)際調(diào)用過(guò)程
void maker()
{
CPMaker *maker = [CPMaker new];
double result = maker.add(5).sub(42).multi(12).divide(23).result;
maker.sub(12);
NSLog(@"result = %f",result);
}
CPMaker要通過(guò)鏈?zhǔn)骄幊?,調(diào)用的對(duì)象函數(shù)返回值必須是Block,同時(shí)該Block的返回值必須是CPMaker的實(shí)例對(duì)象,也就是self。
2.maker.sub(123).add(25)的調(diào)用過(guò)程是怎樣的

如上所示
maker.sub(123) //一共執(zhí)行了兩步操作
可以理解為:
CPMaker *(^sub)(double) = [make sub];//1.獲取Block
subBlock(23);//2.執(zhí)行有參數(shù)的Block
所以我們直接寫maker.sub(123)是不會(huì)報(bào)錯(cuò)或警告的。
maker.sub(123).add(25) 也就不難理解了。
為什么返回值是
Block?
唯有返回值是Block,在調(diào)用的時(shí)候才會(huì)有maker.add(5).sub(42)這樣的寫法。
即可以帶參數(shù)。
如果返回值不是Block,類似于test方法,寫法就是(void)maker.test.test.test;樣式,看著都別扭。如果不加(void)會(huì)警告,顯示get方法沒(méi)有使用起返回值,雖然警告但是可以正常執(zhí)行。
為什么
Block返回值是CPMaker的實(shí)例對(duì)象?
當(dāng)執(zhí)行Block,返回對(duì)象本身,才可以接著調(diào)用其他的鏈?zhǔn)椒椒?,即拼接更多的方法?br> 如:maker.add(5).sub(42).multi(12).divide(23)
3.為什么單獨(dú)寫maker.sub(123)不會(huì)警告,而直接寫maker.count會(huì)警告
maker.sub(123)寫法是正確的,兩步拼成了一句話,看著有點(diǎn)難理解而已。
maker.count表示的是get方法,我們沒(méi)有用他的返回值,所以警告,但是get方法會(huì)正常調(diào)用。
4.更優(yōu)的鏈?zhǔn)骄幊虒懛?/h5>
以maker.add(23)為例,如上寫法有什么問(wèn)題?
點(diǎn)式調(diào)用沒(méi)有參數(shù)類型提示,API設(shè)計(jì)不夠友好
maker.add
什么是更好的設(shè)計(jì)?
//.h文件對(duì)象方法全部替換成readOnly屬性(其實(shí)還是get方法)
@property (nonatomic, readonly) CPMaker *(^add)(double);
@property (nonatomic, readonly) CPMaker *(^sub)(double);
@property (nonatomic, readonly) CPMaker *(^multi)(double);
@property (nonatomic, readonly) CPMaker *(^divide)(double);
//實(shí)際調(diào)用效果有參數(shù)類型的提示
maker.add(double)
5.鏈?zhǔn)骄幊虒?shí)際應(yīng)用
以maker.add(23)為例,如上寫法有什么問(wèn)題?
點(diǎn)式調(diào)用沒(méi)有參數(shù)類型提示,API設(shè)計(jì)不夠友好
maker.add
什么是更好的設(shè)計(jì)?
//.h文件對(duì)象方法全部替換成readOnly屬性(其實(shí)還是get方法)
@property (nonatomic, readonly) CPMaker *(^add)(double);
@property (nonatomic, readonly) CPMaker *(^sub)(double);
@property (nonatomic, readonly) CPMaker *(^multi)(double);
@property (nonatomic, readonly) CPMaker *(^divide)(double);
//實(shí)際調(diào)用效果有參數(shù)類型的提示
maker.add(double)
實(shí)際鏈?zhǔn)骄幊讨?,如下寫法非常失?
CPMaker不該手動(dòng)創(chuàng)建并且暴露在外
void maker()
{
CPMaker *maker = [CPMaker new];
double result = maker.add(5).sub(42).multi(12).divide(23).result;
NSLog(@"result = %f",result);
}
實(shí)際應(yīng)用中可能會(huì)有以下幾種情況:
1.新建容器類,提供類方法,提供鏈?zhǔn)骄幊棠芰?/p>
//.h文件
#import <Foundation/Foundation.h>
#import "CPMaker.h"
@interface CPCalculator : NSObject
//假如有參數(shù),就會(huì)有提示,Xcode新的特性
@property (nonatomic, class, readonly) CPMaker *(^calculator)(void);
@end
//.m文件
#import "CPCalculator.h"
@implementation CPCalculator
+ (CPMaker *(^)(void))calculator
{
return ^(void){
return [CPMaker new];
};
}
@end
//.實(shí)際調(diào)用
CPCalculator.calculator().add(4).sub(5).multi(5).divide(23);
2.為分類提供方法,插入Block。例如Masonry
仿寫Masonry
//1.新建分類方法
#import <UIKit/UIKit.h>
#import "CPDConstraintMaker.h"
@interface UIView (CPDMasonry)
- (void)cpd_updateConstraints:(void(^)(CPDConstraintMaker *))maker;
@end
//2.CPDConstraintMaker內(nèi)部的參數(shù)
#import <Foundation/Foundation.h>
#import "CPDConstraint.h"
@interface CPDConstraintMaker : NSObject
@property (nonatomic, readonly) CPDConstraint *left;
@property (nonatomic, readonly) CPDConstraint *right;
@property (nonatomic, readonly) CPDConstraint *top;
@property (nonatomic, readonly) CPDConstraint *bottom;
@property (nonatomic, readonly) CPDConstraint *width;
@property (nonatomic, readonly) CPDConstraint *height;
@end
//3.CPDConstraint內(nèi)部比較方法
@interface CPDConstraint : NSObject
@property (nonatomic, readonly) CPDConstraint *(^cpd_equalTo)(CGFloat num);
@end
//4.實(shí)際調(diào)用
[self.view cpd_updateConstraints:^(CPDConstraintMaker *maker) {
maker.left.cpd_equalTo(12);
maker.height.cpd_equalTo(4);
}];