Block 的内存管理

点击打开链接

block 有什么意义,特点等等,这些东西,实在是太复杂了,这里只是简单的总结一下block的内存管理。而且也仅仅限于objective-C的部分

Block memory

block 的内存管理,应该是最头疼的地方,就用这个来自WWDC的例子来解释一下吧。

当程序运行到这里时,stack 空间中有 shared 变量和 captured 变量。

这里可以看出,__block 变量开始是处于stack上的。

当程序运行到这里时,stack 空间中有 shared 变量,captured 变量和block1。

这里可以看出,block 类型的变量开始时也是处在stack上的。

当程序运行到这里时,stack 空间中有 shared 变量,captured 变量和block1。

这里值得注意的就是当我们直接修改stack 上的captured变量时,block1中的captured变量仍然是原来的数值10。事实上,从const 我们就可以看出,block1中的captured变量是不能被修改的而且是从stack原有变量的一个const 拷贝。在block1中访问的captured变量是const拷贝的,也就是说block1中captured = 10,而不是原有的stack上的值 20。当然,在block1中,我们也不能修改captured变量。

 

Copy block

block在一开始是处在stack上的,这是为了考虑到效率的原因,但是,有时候是需要block的生命周期长于一开始的stack,这时,我们就通过copy block 来将block复制到heap。

当程序执行完 block2 = [block1 copy];时,__block 类型变量shared,被复制到了heap中,很显然,shared变量需要被block和block2共享(当然还有stack也要共享),而block2被移动到heap中,很可能生命周期会长于stack,所以,shared也被复制到了heap中。而block2中的captured 也被复制到了heap中。

当程序执行完 block3 = [block2 copy];时, 我们看到的是,block2 和block3 其实指向的是同一片内存空间。事实上,block的数据结构中,保存了引用计数,而对于copy到heap中的block 再copy时,行为同普通对象retain一样,会使引用计数+1。那么如果我们对[block retain]会如何呢? 实际上什么都没有发生,至少在现在的runtime版本下。因为retain中,不仅有引用计数+1在,而且retain的返回值,必须同返回调用对象的地址一样,而block的地址是可能变化的(stack or heap),所以,这里retain的行为几乎是被忽略掉的。

当heap中的block变量先于stack被销毁时,如调用 [block2 release]; [block3 release];,heap中的block2,block3 由于引用计数为0 而被销毁,而 __block 变量shared则还在heap中,因为stack还要使用,block1 也要使用。

当heap中的block变量晚于stack时,显然,stack 被清除,function中也啥都没了。

最后,当block2 和block3 都被release之后。则恢复到最初状态

block details

当我们写出一个Block literal expression

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
35
^ { printf ( "hello worldn" ); }
事实上,编译器为我们生成了如下结构
struct __block_literal_1 {
 
     void *isa;
 
     int flags;
 
     int reserved;
 
     void (*invoke)( struct __block_literal_1 *);
 
     struct __block_descriptor_1 *descriptor;
 
}; void __block_invoke_1( struct __block_literal_1 *_block) {
 
     printf ( "hello worldn" );
 
} static struct __block_descriptor_1 {
 
     unsigned long int reserved;
 
     unsigned long int Block_size;
 
} __block_descriptor_1 = { 0, sizeof ( struct __block_literal_1)}; 当Block literal expression 使用时 __block_literal_1 则会被初始化为: struct __block_literal_1 _block_literal = {
 
     &_NSConcreteStackBlock,
 
     (1<<29), <uninitialized>,
 
     __block_invoke_1,
 
     &__block_descriptor_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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int x = 10;
 
void (^vv)( void ) = ^{ printf ( "x is %dn" , x);};
 
x = 11;
 
vv();
 
编译器会生成如下结构
 
struct __block_literal_2{
 
     void *isa;
 
     int flags;
 
     int reserved;
 
     void (*invoke)( struct __block_literal_2 *);
 
     struct __block_descriptor_2 *descriptor;
 
     const int x;
 
};
 
void __block_invoke_2( struct __block_literal_2 *_block){
 
     printf ( "x is %dn" , _block->x);
 
}
 
void struct __block_descriptor_2{
 
     unsigned long int reserved;
 
     unsigned long int block_size;
 
}__block_descriptor_2 = {0, sizeof ( struct __block_literal_2)};
 
struct __block_literal_2 __block_literal_2 = {
 
     & NSConcreteStackBlock ,
 
     (1<<29),
 
     __block_invoke_2,
 
     &__block_descriptor_2,
 
     x
 
};

block中使用的普通变量(int, char *)导入是const copy。普通对象则会retain。__block 类型变量则什么不做,只是保存一个指针,全局变量也只是保存一个简单的指针。

当然,block 可能也会嵌套block,那么又会是什么样子?其实不复杂,复杂的只是增加了复制函数,和释放函数,这一点很像C++的拷贝构造函数,在必要时生成。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
void (^existingBlock)( void ) = …;
 
void (^vv)( void ) = ^{existingBlock();};
 
vv();
 
struct __block_literal_3{
 
     ...; //esisting block
 
};
 
struct __block_literal_4{
 
     void *isa;
 
     int flags;
 
     int reserved;
 
     void (*invoke)( struct __block_literal_4 *);
 
     struct __block_literal_3 * const existingBlock;
 
};
 
void __block_invoke_4( struct __block_literal_3 * __block ) {
 
    __block ->existingBlock->invoke( __block ->existingBlock);
 
}
 
void __block_copy_4( struct __block_literal_4 *dst, struct __block_literal_4 *src) {
 
      //_Block_copy_assign(&dst->existingBlock, src->existingBlock, 0);
 
      _Block_object_assign(&dst->existingBlock, src->existingBlock, BLOCK_FIELD_IS_BLOCK);
 
}
 
void __block_dispose_4( struct __block_literal_4 *src) {
 
      // was _Block_destroy
 
      _Block_object_dispose(src->existingBlock, BLOCK_FIELD_IS_BLOCK);
 
}
 
static struct __block_descriptor_4 {
 
     unsigned long int reserved;
 
     unsigned long int Block_size;
 
     void (*copy_helper)( struct __block_literal_4 *dst, struct __block_literal_4 *src);
 
     void (*dispose_helper)( struct __block_literal_4 *);
 
} __block_descriptor_4 = {
 
     0,
 
     sizeof ( struct __block_literal_4),
 
     __block_copy_4,
 
     __block_dispose_4,
 
};
 
初始化
 
   struct __block_literal_4 _block_literal = {
 
     &_NSConcreteStackBlock,
 
     (1<<25)|(1<<29), <uninitialized>
 
     __block_invoke_4,
 
     &__block_descriptor_4
 
         existingBlock,
 
    };

__block storage variables

__block  变量是一种很特殊的数据类型,有自己的特有的数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct _block_byref_xxxx {
 
     void *isa;
 
     struct _block_byref_xxxx *forwarding;
 
     int flags;   //refcount;
 
     int size;
 
     // helper functions called via Block_copy() and Block_release()
 
     void (*byref_keep)( void  *dst, void *src); //需要时被生成
 
     void (*byref_dispose)( void *); //需要时被生成
 
     typeof(marked_variable) marked_variable;
 
};

看看__block 类型变量的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int __block i = 10;
 
i = 11;
 
struct _block_byref_i {
 
     void *isa;
 
     struct _block_byref_i *forwarding;
 
     int flags;   //refcount;
 
     int size;
 
     int captured_i;
 
} i = { NULL , &i, 0, sizeof ( struct _block_byref_i), 10 };
 
i.forwarding->captured_i = 11;

显然,当block中增加了__block 类型变量之后,嵌套block 的拷贝函数也会增加对__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
33
34
35
36
37
38
39
40
41
42
43
__block void (voidBlock)( void ) = blockA;
 
voidBlock = blockB;
 
struct _block_byref_voidBlock {
 
     void *isa;
 
     struct _block_byref_voidBlock *forwarding;
 
     int flags;   //refcount;
 
     int size;
 
     void (*byref_keep)( struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src);
 
     void (*byref_dispose)( struct _block_byref_voidBlock *);
 
     void (^captured_voidBlock)( void );
 
};
 
void _block_byref_keep_helper( struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src) {
 
     //_Block_copy_assign(&dst->captured_voidBlock, src->captured_voidBlock, 0);
 
     _Block_object_assign(&dst->captured_voidBlock, src->captured_voidBlock, BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);
 
}
 
void _block_byref_dispose_helper( struct _block_byref_voidBlock *param) {
 
     //_Block_destroy(param->captured_voidBlock, 0);
 
     _Block_object_dispose(param->captured_voidBlock, BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER)}
 
struct _block_byref_voidBlock voidBlock = {( .forwarding=&voidBlock, .flags=(1<<25), .size= sizeof ( struct _block_byref_voidBlock *),
 
       .byref_keep=_block_byref_keep_helper, .byref_dispose=_block_byref_dispose_helper,
 
       .captured_voidBlock=blockA )};
 
voidBlock.forwarding->captured_voidBlock = blockB;

block中,引入了__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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
int __block i = 2;
 
functioncall(^{ i = 10; });
 
struct _block_byref_i {
 
     void *isa;  // set to NULL
 
     struct _block_byref_voidBlock *forwarding;
 
     int flags;   //refcount;
 
     int size;
 
     void (*byref_keep)( struct _block_byref_i *dst, struct _block_byref_i *src);
 
     void (*byref_dispose)( struct _block_byref_i *);
 
     int captured_i;
 
};
 
struct __block_literal_5 {
 
     void *isa;
 
     int flags;
 
     int reserved;
 
     void (*invoke)( struct __block_literal_5 *);
 
     struct __block_descriptor_5 *descriptor;
 
     struct _block_byref_i *i_holder;
 
};
 
void __block_invoke_5( struct __block_literal_5 *_block) {
 
    _block->i_holder->forwarding->captured_i = 10;
 
}
 
void __block_copy_5( struct __block_literal_5 *dst, struct __block_literal_5 *src) {
 
      _Block_object_assign(&dst->i_holder, src->i_holder, BLOCK_FIELD_IS_BYREF | BLOCK_BYREF_CALLER);
 
}
 
void __block_dispose_5( struct __block_literal_5 *src) {
 
      _Block_object_dispose(src->i_holder, BLOCK_FIELD_IS_BYREF | BLOCK_BYREF_CALLER);
 
}
 
static struct __block_descriptor_5 {
 
     unsigned long int reserved;
 
     unsigned long int Block_size;
 
     void (*copy_helper)( struct __block_literal_5 *dst, struct __block_literal_5 *src);
 
     void (*dispose_helper)( struct __block_literal_5 *);
 
} __block_descriptor_5 = { 0, sizeof ( struct __block_literal_5) __block_copy_5, __block_dispose_5 };
 
struct _block_byref_i i = {( .forwarding=&i, .flags=0, .size= sizeof ( struct _block_byref_i) )};
 
struct __block_literal_5 _block_literal = {
 
     &_NSConcreteStackBlock,
 
     (1<<25)|(1<<29), <uninitialized>,
 
     __block_invoke_5,
 
     &__block_descriptor_5,
 
         2,
 
    };

block 中的太多细节这里不做赘述,有兴趣的可以参考Block—ABI-Apple,也可以直接这里去看。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值