什么是block
block就是一个里面存储了指向函数体中包含定义block时的代码块的函数指针,以及block外部上下文变量等信息的结构体。
block内存分布
三种类型block
在Objective-C语言中,有三种类型的block。
1,__NSGlobalBlock__ 保存在全局区,不会访问任何外部变量。
2,__NSStackBlock__ 保存在栈中,当函数返回时会被销毁。
3,__NSMallocBlock__ 保存在堆中,当引用计数为0时会被销毁。
在MRC模式下,运行如下代码,从log中可以证明存在三种类型的block。
1 | - (void)viewDidLoad { |
log信息:
1 | __NSGlobalBlock__ |
对三种类型的block实例发送消息
如何判断block类型
我们知道有三种类型block,那么如何判断一个block是属于哪种的呢?依据如下规则即可。
没有使用外部变量的(外部变量不包括全局变量和静态变量)block,在全局区,属于__NSGlobalBlock__。
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
33
34// 在MRC模式下运行
typedef void(^TestBlock)(void);
int globalVal = 666;
static int staticVal = 888;
@interface DemoViewController ()
@property(nonatomic, strong)TestBlock strongBlock;
@property(nonatomic, copy)TestBlock copyBlock;
@property(nonatomic, weak)TestBlock weakBlock;
@end
@implementation DemoViewController
- (void)viewDidLoad {
[super viewDidLoad];
_strongBlock = ^{
NSLog(@"hello world!");
};
_copyBlock = ^{
NSLog(@"_copyBlock %d", globalVal);
};
_weakBlock = ^{
NSLog(@"_weakBlock %d", staticVal);
};
NSLog(@"_strongBlock %s", object_getClassName(_strongBlock));
NSLog(@"_copyBlock %s", object_getClassName(_copyBlock));
NSLog(@"_weakBlock %s", object_getClassName(_weakBlock));
}
@endlog信息显示如下:
1
2
3_strongBlock __NSGlobalBlock__
_copyBlock __NSGlobalBlock__
_weakBlock __NSGlobalBlock__使用外部变量的(外部变量不包括全局变量和静态变量)block,在栈区,属于__ NSStackBlock__。
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
33// 在MRC模式下运行
typedef void(^TestBlock)(void);
@interface DemoViewController ()
@property(nonatomic, strong)TestBlock strongBlock;
@property(nonatomic, copy)TestBlock copyBlock;
@property(nonatomic, weak)TestBlock weakBlock;
@end
@implementation DemoViewController
- (void)viewDidLoad {
[super viewDidLoad];
int tempV = 100;
_strongBlock = ^{
NSLog(@"_strongBlock %d", tempV);
};
_copyBlock = ^{
NSLog(@"_copyBlock %d", tempV);
};
_weakBlock = ^{
NSLog(@"_weakBlock %d", tempV);
};
NSLog(@"_strongBlock %s", object_getClassName(_strongBlock));
NSLog(@"_copyBlock %s", object_getClassName(_copyBlock));
NSLog(@"_weakBlock %s", object_getClassName(_weakBlock));
}
@endlog信息显示如下:
1
2
3_strongBlock __NSStackBlock__
_copyBlock __NSStackBlock__
_weakBlock __NSStackBlock__对栈区block进行copy之后,得到新的block,在堆区,属于__ NSMallocBlock__。
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
33// 在MRC模式下运行
typedef void(^TestBlock)(void);
@interface DemoViewController ()
@property(nonatomic, strong)TestBlock strongBlock;
@property(nonatomic, copy)TestBlock copyBlock;
@property(nonatomic, weak)TestBlock weakBlock;
@end
@implementation DemoViewController
- (void)viewDidLoad {
[super viewDidLoad];
int tempV = 100;
_strongBlock = ^{
NSLog(@"_strongBlock %d", tempV);
};
_copyBlock = ^{
NSLog(@"_copyBlock %d", tempV);
};
_weakBlock = ^{
NSLog(@"_weakBlock %d", tempV);
};
NSLog(@"_strongBlock copy %s", object_getClassName([_strongBlock copy]));
NSLog(@"_copyBlock copy %s", object_getClassName([_copyBlock copy]));
NSLog(@"_weakBlock copy %s", object_getClassName([_weakBlock copy]));
}
@endlog信息显示如下:
1
2
3_strongBlock copy __NSMallocBlock__
_copyBlock copy __NSMallocBlock__
_weakBlock copy __NSMallocBlock__
ARC对block的影响
ARC会自动把strong类型且捕获外部变量的block从栈copy到堆中。
1 | // 在ARC模式下运行 |
log信息显示如下:
1 | _strongBlock __NSMallocBlock__ |
根据log信息可以判断strong、copy修饰的或者局部变量引用的block实例,都属于__NSMallocBlock__。
原因:_strongBlock、_copyBlock和_localBlock都使用了外部变量,属于栈区block,但是在ARC模式下,被strong或copy等非weak修饰符修饰的栈区block,会自动copy到堆区,变成__NSMallocBlock__。(block实例赋值给局部变量_localBlock,就相当于被变量强引用=strong,所以_localBlock是__NSMallocBlock__)
变量的复制
对于 block 外的变量引用,block 默认是将其复制到其数据结构中来实现访问的,如果对象是引用类型,则block会将其引用计数加一 ,如下图所示:
对于用 __block 修饰的外部变量引用,block 是复制其引用地址来实现访问的,如下图所示: