文章目录
  1. 1. Blocks的使用
    1. 1.1. 简介
    2. 1.2. 闭包性
    3. 1.3. 语法
      1. 1.3.1. 作为一个局部变量
    4. 1.4. 作为一个属性
    5. 1.5. 作为一个方法参数
    6. 1.6. 作为参数的方法调用
    7. 1.7. 作为一个类型定义

Blocks的使用

Blocks无论是在Objective-C还是Swift都被大量的应用,尤其是将函数作为一等公民的Swift。今天先分享给大家Blocks的集中语法,以及什么是Blocks;

简介

Blocks是C语言的一种扩展,被称为带自动变量的匿名函数,就是没有函数名的函数,在C语言中这种函数是肯定不允许存在的,但是在C语言中也可以实现,那就是通过函数指针,但是通过函数指针也需要知道函数名是什么,就像这样:

1
2
3
int addNum(int num);
int (*funcptr)(int) = &addNum;
int result = (*funcptr)(10);

但是在Blocks中我们可以使用匿名函数(不带名的函数),这样有什么好处?其实最基本的就是对我们的工作来说,每天做的事情就是在围绕函数名,变量名,属性名,类明…..。因此听起来还是挺好用的。

闭包性

在计算机科学中,Blocks拥有的特性被称为闭包,那么什么是闭包呢?

闭包是指可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。在PHP、Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python、Go、Lua、objective c、swift 以及Java(Java8及以上)等语言中都能找到对闭包不同程度的支持。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int buttonId = 0;

void buttonCallBack(int event){
printf("buttonId:%d event:%d \n",buttonId,event);
}

int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here..

buttonCallBack(1);
}
return 0;
}

代码没有问题,仅仅是调用一个按钮输出结果:buttonId:0 event:1

但是如果切换为多个按钮?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#import <Foundation/Foundation.h>

int buttonId;

void buttonCallBack(int event){

printf("buttonId:%d event:%d \n",buttonId,event);
}

void setButtonCallBacks(){
for (int i = 0; i<10; i++) {
buttonId = i;
setButtonCallBack(i,&buttonCallBack);
}
}

int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here..

buttonCallBack(1);
}
return 0;
}

一个很明显的问题,我们无论怎么触发我们的按钮都会产生一个结果,就是调用第10个按钮,为什么?原因很简单就是因为buttonId是一个全局变量所在造成的。解决办法,很简单,给buttonCallBack增加一个id参数。

当然我们可以发挥面向对象的特性,将我们的每一个按钮变成多个对象,每个对象持有自己的id属性就可以解决了。 这种增加代码量的方案不是太好。

这个时候我们用Block是来实现。

1
2
3
4
5
void setButtonCallBacks(){
for (int i = 0; i<10; i++) {
setButtonCallBackUsingBlok(i,^(int event){
printf("buttonId:%d event:%d \n",buttonId,event);
});

简单的一段代码也可以解决我们刚才产生的问题,这段代码将带有自动变量i的匿名函数设定为按钮的回调。

使用Blocks可以不声明Objective-C类,也没有使用静态变量,或者静态全局变量或者是全局变量的问题。这一概念就是闭包性(Blocks)

语法

基本上Blocks的语法为以下几部分:

  • 作为一个局部变量
  • 作为一个属性
  • 作为一个方法参数
  • 作为参数的方法调用
  • 作为一个类型定义

如果你发现自己想要的语法不在这里,那么它可能是一种类型定义,增加代码的可读性。

作为一个局部变量

语法:返回类型(^ 块名称)(parameterTypes)= ^ 返回类型(参数){…};

示例:

1
2
3
4
5
6
7
8
9
10
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here..
int (^blk)(int count) = ^(int count){
return count+1;
};
NSLog(@"%d",blk(1));
}
return 0;
}

因此既然为变量,我们也可以赋值使用

1
2
int (^blk2)(int );
blk2 = blk1;

作为一个属性

语法:@property(nonatomic, copy)返回类型(^块名称)(parameterTypes);

示例:

1
@property(nonatomic,copy) int (^blk)(int count);
1
2
3
4
5
6
7
-(void)message{
self.blk = ^(int count){
return count+1;
};

self.blk(1);
}

(PS:我们在定义blocks属性时候他的关键字应该是copy。因为一个block需要被拷贝来跟踪他捕获原始范围以外的状态。当用ARC的时候我们需要担心,因为这是自动发生的。但是最好还是给属性指定关键字来表明他的行为)

作为一个方法参数

语法:- (void)someMethodThatTakesABlock:(返回类型 (^)(parameterTypes))块名称;

示例:

1
2
3
- (void)someMethodThatTakesABlock:(int (^)(int count))blk{
NSLog(@"%d",blk(1));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here..
int (^blk)(int count) = ^(int count){
return count+1;
};

Receiver *receiver = [[Receiver alloc]init];
[receiver someMethodThatTakesABlock:blk];


}
return 0;
}

可以看到,我们使用了Blocks作为了一个函数参数来处理

作为参数的方法调用

语法:[someObject someMethodThatTakesABlock:^ 返回类型(参数){…}];

示例:

1
2
3
- (void)someMethodThatTakesABlock:(int (^)(int count))blk{
NSLog(@"%d",blk(1));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here..
int (^blk)(int count) = ^(int count){
return count+1;
};

Receiver *receiver = [[Receiver alloc]init];
[receiver someMethodThatTakesABlock:^int(int count){
return blk(10);
}];

}
return 0;
}

PS:有一些人说的Blocks回调就是通过这种实现的

作为一个类型定义

语法:

typedef的返回类型(^ 类型名)(parameterTypes);

类型名块名称= ^ 返回类型(参数){…};

示例:

1
2
3
- (void)someMethodThatTakesABlock:(int (^)(int count))blk{
NSLog(@"%d",blk(1));
}
1
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here..
       
        typedef int (^blk_t)(int);
        blk_t blk = ^(int count){
            return count+1;
        };

        
        Receiver *receiver = [[Receiver alloc]init];
        [receiver someMethodThatTakesABlock:blk];
    }
    return 0;
}

PS;如果你发现自己需要的语法不在此列,它很可能是一个类型定义将使你的代码更易读。

文章目录
  1. 1. Blocks的使用
    1. 1.1. 简介
    2. 1.2. 闭包性
    3. 1.3. 语法
      1. 1.3.1. 作为一个局部变量
    4. 1.4. 作为一个属性
    5. 1.5. 作为一个方法参数
    6. 1.6. 作为参数的方法调用
    7. 1.7. 作为一个类型定义