一、概述

Block是C级别的语法和运行时特性。Block比较类似C函数,但是Block比之C函数,其灵活性体现在栈内存、堆内存的引用,我们甚至可以将一个Block作为参数传给其他的函数或者Block(嵌套)。在实际开发中,Block是使用非常广泛的,可以说它与GCD是绝配。如果GCD没有了Block,也许一切都不一样了!

二、声明和使用Block

我们使用操作符来声明一个block变量和表明block的开始。Block体是由花括号{开始到花括号}结束。比如下面一个小例子:

 


1

2

3

4

5

6



 

intmultiplier=7;

int(^myBlock)(int)=^(intnum){

returnnum *multiplier;

};// 变量声明是需要;的哦!

 


描述如下:

可能您也注意到了,block可以使用同一作用域范围的变量,比如上面的multiplier与myBlock变量是同一作用域的。

如果我们声明block为变量,那么我们可以像函数调用一样来调用它:

 

 


1

2

3

4

5

6

7

8

9



 

intmultiplier=7;

int(^myBlock)(int)=^(intnum){

returnnum *multiplier;

};

 

printf("%d",myBlock(3));

// prints "21"

 


三、直接使用block

在很多时候,我们并不需要声明block变量;相反,我们更多地是直接简单地写block内联体作为参数。下面我们通过qsort_b这个c函数来实现快速排序,它的最后一个参数是一个闭包,用于决定ASC还是DSC排序:

 


1

2

3

4

5

6

7

8

9

10

11

12

13

14



 

char*names[4]={"henishuo.com","weibo.com/huangyibiao520","GITHUB","CSDN"};

 

qsort_b(names,4,sizeof(char*),^int(constvoid*name1,constvoid*name2){

char*lhs=*(char**)name1;

char*rhs=*(char**)name2;

        

returnstrcmp(lhs,rhs);

});

 

for(NSUIntegeri=0;i<4;++i){

NSLog(@"%s",names[i]);

}

 


四、Cocoa中的Block

在Cocoa中有很多的方法是使用block作为参数的,最典型的就是集合对象的操作。比如,最常见的就是NSArray的排序API,需要指定一个排序规则,都是通过block来实现的。比如下面的排序:

 


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

28

29

30

31

32



 

NSArray*stringsArray=@[@"string 1",

@"String 21",

@"string 12",

@"String 11",

@"String 02"];

 

NSStringCompareOptionscomparisonOptions=NSCaseInsensitiveSearch

|NSNumericSearch

|NSWidthInsensitiveSearch

|NSForcedOrderingSearch;

 

NSLocale*currentLocale=[NSLocalecurrentLocale];

 

NSArray*finderSortArray=[stringsArray:^NSComparisonResult(id_Nonnull obj1,id_Nonnull obj2){

NSRangestring1Range=NSMakeRange(0,[obj1 length]);

return[obj1:obj2:comparisonOptions:string1Range:currentLocale];

}];

 

NSLog(@"finderSortArray: %@",finderSortArray);

 

// 打印结果如下:

/*

2016-03-25 10:08:18.880 BlockDemos[8077:1168458] finderSortArray: (

    "string 1",

    "String 02",

    "String 11",

    "string 12",

    "String 21"

)

*/

 


五、__block变量

block可以在block体内修改有有效作用域范围的外部变量的值,它是block非常强大的特性。对于需要修改的外部变量,需要使用__block来标识。

 


1

2

3

4

5

6

7

8

9

10

11

12



 

__block intcount=0;

void(^TestBlock)(BOOL)=^(BOOLisOk){

if(isOk){

// 如果没有加__block来声明,编译不通过

count++;

}

};

 

TestBlock(YES);

NSLog(@"count = %d",count);

 


六、typedef block别名

因为把block全写出来不太方便,而且在开发中使用block作为函数的参数时,直接将整个block写在参数位置,可读性明显降低,那么有什么办法可以解决这个问题呢?

答案就是使用typedef给block起一个别名。

下面是我的项目中经常使用的几个block别名:

 


1

2

3

4

5

6

7

8



 

// 请求成功的回调

typedefvoid(^HYBResponseSuccessBlock)(idresponse);

// 请求失败的回调

typedefvoid(^HYBResponseFailureBlock)(NSError*error);

// 状态标识公用block

typedefvoid(^HYBStatusBlock)(BOOLstatus);

 


这么写有什么好处?语义清晰了,而且书写起来也简单多了。

七、block作为参数返回值

有时候,我们会直接返回一个block供外部调用,这时候就需要它了:

 


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16



 

typedefvoid(^HYBStatusBlock)(BOOL);

-(HYBStatusBlock)testStatus{

HYBStatusBlockblock=^(BOOLstatus){

NSLog(@"status is %d",status);

};

  

// 将block copy到堆上

return[block copy];

}

 

HYBStatusBlockblock=[selftestStatus];

block(YES);

 

// status is 1

 


八、block作为成员变量

在开发中,我们见到很多block作为类的成员变量,用来代替代理的。比如,从控制器A进入到控制器B,在B操作完成后需要反馈给A,这时候使用block就很简化了!

在A控制器中调起B控制器:


1

2

3

4

5

6

7

8



 

B *b=[[Balloc]:^(BOOLisSuccess){

// B在完成时,反馈到A

// 刷新状态

}];

 

[self.navigationController:b:YES];

 


在B中声明了一个block属性:

 


1

2

3

4



 

// 为什么使用copy?这是拷贝到堆内存上

@property(nonatomic,copy)HYBStatusBlockcallback;

 


然后在B操作完成时,回调A

 


1

2

3

4

5



 

if(self.callback){

self.callback(YES);// NO

}