Block 详解

概述

iOS SDK 4.0开始,Apple引入了block这一特性,它是带有自动变量的匿名函数。先来看看block的经典用法:

1
2
3
[UIView animateWithDuration:0.3 animations:^{
//code
}];

定义block变量

关于block的语法,请使劲戳这里-> 语法

返回值类型 (^变量名)(参数类型 …) = ^ 返回值类型 (参数类型 入参名 …) {return 返回值}

1
2
3
4
5
6
7
8
9
// even or uneven
BOOL (^isInputEven)(int) = ^(int input) {
if (input % 2 == 0) {
return YES;
} else {
return NO;
}
};
NSLog(@"%d %@ number", x, isInputEven(3) ? @"is an even" : @"is not an even");

返回值->BOOL 名称-> isInputEven 参数-> int 入参-> input

全局变量

1
2
3
4
5
6
7
8
9
10
11
12
...
@property (nonatomic,assign) int count;
...
self.count = 10;
//防止循环引用
__weak typeof(self) weakSelf = self;
void (^result)(void) = ^() {
NSLog(@"block内部全局变量count:%d",weakSelf.count);
};
result();
self.count = 20;
NSLog(@"block外部全局变量count:%d",self.count);

局部变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int x = 25;
void (^block)(void) = ^() {
printf("x = %d\n", x);
};
block();
x = 1;
block();
编译运行后得到的输出同样是两个25

//将局部变量声明为__block,表示外部变化将会在block内进行同样操作
__block int y = 50;
void (^blockB)(void) = ^() {
printf("y = %d\n", y);
};
blockB();
y = 2;
blockB();
编译运行后得到的输出50 2

typedef block

1
2
3
4
5
6
typedef int (^blockName)(int param);

@property (nonatomic,copy) blockName block;
block = ^(int param) {
return param * param;
}

Swift 逃逸闭包(@escaping)

逃逸闭包:当函数执行结束后,才去调用函数内部的闭包,叫做逃逸闭包@escaping

非逃逸闭包:当函数执行过程中,执行的函数内部的闭包,叫做非逃逸闭包 @noescape

从 Swift 3开始, non-escaping 闭包闭包默认声明为 non-escaping , 如果你想让参数闭包逃逸的话, 你需要加上一个 @escaping 去修饰类型. 上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

var count = 100
override func viewDidLoad() {
super.viewDidLoad()
closureEscaping {[weak self] in
guard let `self` = self else {return}
print(self.count)// 输出200
}
closureNoescaping {[weak self] in
guard let `self` = self else {return}
print(self.count) //输出300
}
}
func closureEscaping(_ completion: @escaping () -> Swift.Void){
DispatchQueue.main.async {
completion()
}
self.count = 200
}
func closureNoescaping(_ completion: () -> Swift.Void){
completion()
self.count = 300
}

block 内存

_NSConcreteGlobalBlock 静态区(全局区)block:这是一种特殊的bloclk,因为不引用外部变量或引用全局变量、静态变量而存在。另外,作为静态区的对象,它的释放是有操作系统控制的。

_NSConcreteStackBlock 栈区block:位于内存的栈区,一般作为函数的参数出现。

_NSConcreteMallocBlock 堆区block:位于内存的堆区,一般作为对象的property出现。

如果一个blcok引用了外部变量是栈block,则其不引用外部变量就成为了静态blcok。
如果一个block引用了外部变量是堆block,则其不引用外部变量就成为了静态block。
深入 请看霜神: 深入研究Block捕获外部变量和__block实现原理深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用

笔记

  • block的实质是什么?一共有几种block?都是什么情况下生成的?
  • 为什么在默认情况下无法修改被block捕获的变量? __block都做了什么?
  • -
    • 自动变量是以值传递方式传递到Block的构造函数里面去的。Block只捕获Block中会用到的变量。由于只捕获了自动变量的值,并非内存地址,所以Block内部不能改变自动变量的值
    • 一是传递内存地址指针到Block中(例:NSMutableString拼接),二是改变存储区方式(__block)。
  • 模拟一下循环引用的一个情况?block实现界面反向传值如何实现?