链式编程是什么
链式编程就是将多个方法调用用点语法连接起来,让代码更加简洁,可读性更高。
刚开始接触链式编程是 Masonry,用起来真的非常爽。
1
| make.left.right.top.equalTo(self.view);
|
这样一句语句就调用了 4 个方法:
.left 调用了 left 属性的 get 方法
.right、.top 调用了 right 和 top 方法
.equalTo() 调用了 equalTo 方法
这种写法极大简化了写约束的方式。
原理
原理就是调用的属性类型,或者方法返回类型,仍然是原调用对象的类型。
例如 UILabel 调用了某个方法或者属性,得到的类型还是 UILabel,那么还可以继续调用 UILabel 的属性或者方法。
如何实现
看了 Masonry,我发现有两种实现方式。
1.用点语法调用方法
这个其实我之前没发现,写习惯了用方括号调用方法。
例如创建一个 label 可以这样写:UILabel *label = [[UILabel alloc] init]。
其实也可以这样写:UILabel *label = UILabel.alloc.init。
不过后种方法几乎没人用,苹果应该也不推荐这种写法,因为有时候这样写是没有代码提示的。
这种方式还有一个缺点,就是不能调用有参数的方法,所以我们只能写没有参数的方法。
创建一个 UIButton 的分类,写两个方法:
1 2
| - (UIButton *)setTextHello; - (UIButton *)setTextColorRed;
|
并且实现
1 2 3 4 5 6 7 8 9 10 11
| - (UIButton *)setTextColorRed { [self setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; return self; }
- (UIButton *)setImage { [self setImage:[UIImage imageNamed:@"Stan1"] forState:UIControlStateNormal]; return self; }
|
然后我们创建一个按钮的时候就可以这样写:
1 2 3 4 5
| //创建一个按钮 UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoLight]; button.frame = CGRectMake(100, 100, 100, 100); button.setTextHello.setTextColorRed; [self.view addSubview:button];
|
效果如图

但是会报一个警告,因为调用的是属性,但是这个属性没有被用到。
解决方法是在调用属性前面加 (void),这样就可以了。

2.用属性调用
新创建一个 UILabel 的分类。
如果要传入参数的话,就返回一个 UILabel 的 block,可以在 block 里面实现你想要实现的东西。
1 2 3
| @property(nonatomic,copy) UILabel* (^kText)(NSString *text); @property(nonatomic,copy) UILabel* (^kFont)(NSUInteger fontSize); @property(nonatomic,copy) UILabel* (^kTextColor)(UIColor *color);
|
加 k 是为了易于区分,可以不加的。
因为这是在分类里面的属性,不会生成 setter 和 getter 方法,所以都要自己写。
实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| - (UILabel *(^)(NSUInteger))kFont { return ^(NSUInteger font){ [self setFont:[UIFont systemFontOfSize:font]]; return self; }; }
- (UILabel *(^)(NSString *))kText { return ^(NSString *text){ [self setText:text]; return self; }; }
- (UILabel *(^)(UIColor *))kTextColor { return ^(UIColor *color){ [self setTextColor:color]; return self; }; }
- (void)setKFont:(UILabel *(^)(NSUInteger))kFont{} - (void)setKText:(UILabel *(^)(NSString *))kText{} - (void)setKTextColor:(UILabel *(^)(UIColor *))kTextColor{}
|
不会用到 setter 方法,也不能用,所以 setter 方法设为空。
然后就能愉快地链式编程了。

然后我再想,假如没有参数呢,刚开始是想 block 没有参数。
1 2 3
| @property(nonatomic,copy) UILabel* (^aText)(); @property(nonatomic,copy) UILabel* (^aFont)(); @property(nonatomic,copy) UILabel* (^aTextColor)();
|
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| - (UILabel *(^)())aFont { return ^(){ [self setFont:[UIFont systemFontOfSize:16.0f]]; return self; }; }
- (UILabel *(^)())aTextColor { return ^(){ [self setTextColor:[UIColor cyanColor]]; return self; }; }
- (UILabel *(^)())aText { return ^(){ [self setText:@"这还是一个label"]; return self; }; }
- (void)setAFont:(UILabel *(^)())aFont{} - (void)setAText:(UILabel *(^)())aText{} - (void)setATextColor:(UILabel *(^)())aTextColor{}
|
后来发现调用的时候还是要这样:

尝试了一下,最后一个调用能去掉括号,会报警告,能运行,但是没效果。
所以我想,为什么不直接调用属性。
1 2 3
| @property(nonatomic,strong) UILabel *bText; @property(nonatomic,strong) UILabel *bFont; @property(nonatomic,strong) UILabel *bTextColor;
|
getter && setter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| - (UILabel *)bText { [self setText:@"labellabel"]; return self; }
- (UILabel *)bTextColor { [self setTextColor:[UIColor purpleColor]]; return self; }
- (UILabel *)bFont { [self setFont:[UIFont systemFontOfSize:13.0f]]; return self; }
- (void)setBFont:(UILabel *)bFont{} - (void)setBText:(UILabel *)bText{} - (void)setBTextColor:(UILabel *)bTextColor{}
|
调用:
1 2 3
| UILabel *label3 = [[UILabel alloc] initWithFrame:CGRectMake(100, 400, 200, 100)]; (void)label3.bTextColor.bText.bFont; [self.view addSubview:label3];
|
如果调用不加 (void) 还是会报警告。
如果想像 Masonry 那样的链式编程,可以这样写:
1 2 3
| UILabel *label3 = [[UILabel alloc] initWithFrame:CGRectMake(100, 400, 200, 100)]; label3.bTextColor.bText.kFont(30); [self.view addSubview:label3];
|
这样就不会报警告,整洁清晰。