http://www.knowsky.com/884137.html

因为不好读。block和其他语言的匿名函数一样,很多程序员刚开始很难主动去用他。

  本文描述block作为属性的实际使用,看懂block,并讲解如何利用block实现链式编程方法。

  【一】遭遇

  到今天iOS开发中最常用的语言还是objc,市场就像泰坦尼克号,人虽然在上楼,但是船在下沉,所以人还是在下沉。虽然swift出现的迅猛,但是大部分开发者面对的还是objc。什么时候swift替代objc,今天的objc开发人员也不用太着急,说不定船都沉了(苹果保佑)。

  objc最令人头疼的是他看起来像部落语言一样的表达。比如下面这行新手代码,但是就像求偶讯号一样晦涩:


[[NSMutableDictionary alloc] initWithContentsOfURL:[NSURL URLWithString:@"a.txt"]][@"persons"][12];


  但它并没有告诉别人任何有意义的事情,分解一下语法就像下面一样:


NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithContentsOfURL:[NSURL URLWithString:@"a.txt"]];
NSArray* arr = dict[@"persons"];
arr[12];


  语言发展到今天,代码的普世价值不是效率,而是开发效率。这就是为什么java能够成为王者的原因。因为java写起来快,好读。

  【二】探索

  objc比起java又慢又不好读。那么如何让objc看起来像java一样呢。纵观objc全部语法,只有block能够办到。我们来看block的定义


returnType  (^name)  (val1, val2, ...);
 返回类型     变量名        参数列表

//我们可以从表面认为他是一根指向函数的指针,这个函数的样子如下
(returnType)name(val1, val2, ...)
{
  //......
}


可以作为类的属性被'点'出来。第二:可以当作函数直接调用。下面逐个解释,第一个类的属性可以点出来,比如person.name;这很好理解,你一定见过,str.length;对吧。第二个呢,block作为一个变量,但是又可以把它当作指向函数的指针一样调用。


NSString*(^myBlock)() = ^(){ return @"菊乐"; };//定义一个返回值为NSString类型,无参的,并且名字叫做myBlock的block

myBlock();//这一行就是调用

NSString result = myBlock();//这是取出block执行后拿到的返回值,也就是@"菊乐"


  这里再解释一次block的定义:上面的myBlock可以认为是指向一个定义如下的函数的指针,这个函数是


(NSString*) myBlock()
{
   //...      
}

//指向函数的指针
NSString* (*p) ();

//可以认为指针p就是myBlock(其实block的真实身份更加复杂)


  【三】推理

  我们来幻想一下objc写起来是这样的


//设置视图位置和大小,设置视图背景色
view.setFrame(0,0,50,50).setBackgroundColor( @"#0c0c0c".toColor() );
//移除空格并在控制台打印字诗句
@" 白 日 依 山 尽 , 黄 河 入 海 流 ".removeStr(@" ").nslog();


就这样错过了一次摇一摇的机会,人生在缓慢的艰难,而我们却还一无所知。


view.frame = CGRectMake(0, 0, 50, 50);
view.backgroundColor= [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];
    
NSString* str= @" 白 日 依 山 尽 , 黄 河 入 海 流 ";
str= [str stringByReplacingCharactersInRange:@" " withString:@""];
NSLog(str);


  现在我们来倒着分析如何使用block来实现链式编程,再来观察这句话:


view.setFrame(0,0,50,50).setBackgroundColor( @"#0c0c0c".toColor() );


).setBackgroundColor();怎么可以连续点出来,这刚开始很不好理解。这并不难,我们往前看那句话:
 
NSString result = myBlock();


  细心的去发现,block是可以有返回值的,那么我们就依靠这个返回值就可以点出一堆属性,一直点下去,这样我们的链式编程的思路就通了。

  【四】实践

).setBackgroundColor();

  首先准备一个分类。头文件定义如下:


//  UIView+Extension.h

#import <UIKit/UIKit.h>

@interface UIView(Extesion)
@PRoperty (nonatomic,copy) UIView* (^setFrame)(CGFloat x, CGFloat y, CGFloat w, CGFloat h);
@property (nonatomic,copy) UIView* (^setBackgroundColor)(UIColor* color);
@end


setFrame和setBackgroundColor,这意味着只要是继承自UIView的对象就可以点出来这两个属性。

  现在我们需要理解一下两个的区别:


view.setFrame;//这是获取属性,它返回一个block
view.setFrame();//这是获取属性,它返回一个block,最后我们使用括号调用了这个block,这个block的返回值是UIView类型的对象,那么他就可以继续点出下一个属性了


setFrame()这句话的末尾因为加上了括号所以执行了block,而他的返回类型是一个UIView对象,所以可以调用setBackgroundColor();

  这里解释一下为什么用copy,block这种类型本来是值类型的,他本来是在栈上的,在ARC环境下如果被任何强指针过了一次,编译器就会把他进行一次copy,放到堆内存中。block的retain行为默认是用copy的行为实现的,因为block变量默认是声明为栈变量的,为了能够在block的声明域外使用应该使用copy。

   接下来实现.m文件内的代码:


- (UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame
{
    return ?;
}


block对象他能够执行一些功能就够了。所以属性内部返回一个临时block对象,这个block内部呢去执行一些功能,最重要的是block内部一定要返回UIView类型的对象。编译器会对block的类型进行苛刻的检测。


- (UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame
{
    return ^(CGFloat x, CGFloat y, CGFloat w, CGFloat h){//返回临时变量的block
        self.frame = CGRectMake(x, y, w, h);//block执行一些功能
        return self;//block执行完毕的返回值
    };
}


  下面是.m文件的实现


//  UIView+Extension.m

#import "UIView+Extension.h"

@implementation UIView(Extesion)
- (UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame
{
    return ^(CGFloat x, CGFloat y, CGFloat w, CGFloat h){
        self.frame = CGRectMake(x, y, w, h);
        return self;
    };
}
- (void)setSetFrame:(UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame{};//该属性不需要从外部设置,不想报警告就写空方法

- (UIView *(^)(UIColor *))setBackgroundColor
{
    return ^(UIColor* color){
        self.backgroundColor= color;
        return self;
    };
}
- (void)setSetBackgroundColor:(UIView *(^)(UIColor *))setBackgroundColor{};
@end


  【五】崩溃

  objc中向nil对象发送任何消息都不会崩溃,但是发送他不能处理的消息类型就会崩溃,这也是最经常遇到了情况。还有这样也会崩溃:nil.length; view.length;调用不存在的属性也会崩溃。那么这是我们要处理的一个重要的情况。

  试想,一行代码中间突然有一个处理返回了nil给下一个环节,一调用就崩溃,关键是断点并不能明确告诉你在哪一行。我们只能从控制台中获得调试信息。这也是链式编程的弊端之一。

链式编程框架LinkBlock来统一处理,针对几乎所有常用功能进行了链式的封装,并没有什么门槛,希望大家帮我点一颗星星,支持天朝做良好的程序员。

[希望大家帮我点一颗星星]

  【六】效率

  关于效率,肯定比原生低那么一丁点的,几乎可以忽略的,只是多了一次属性调用,一次一临时block的创建,一次block的执行。博主的观点是为了提高开发效率,而降低一些运行效率。有时候也是值得的。最后苹果也是大力提倡我们使用block的。祝愉快。