OC语言 - block:内存分布

copy | retain | release

1 - block 在内存中的存储状况分为 NSGlobalBlock、NSStackBlock 和 NSMallocBlock 三种

2 - 对于 block 而言,我们对 block 不管是 retain、copy、release 都不会改变其引用计数:retainCount 始终是 1。block_copy 和 copy 等效;block_release 和 release 等效

① NSMallocBlock 支持 retain、release!虽然 retainCount 始终是 1,但内存管理器中引用计数仍然会增加、减少。堆中的 block 进行 copy 后不会生成新的对象,只是增加了一次引用,相当于 retain

② NSGlobalBlock:retain、copy、release 操作都无效

③ NSStackBlock:retain、release 等操作无效,copy 除外!因为 NSStackBlock 在函数返回后其内存就被回收,即使把栈中的 block 使用 retain 也完全没任何用,但是栈区中的 block 经 copy 后会生成新的 NSMallocBlock 对象

注:尽量不要对 block 进行 retain 操作

3 - MRC 中容易犯的错误是 [[mutableAarry addObject:stackBlock] 原因在于当函数出栈后,从 mutableAarry 中取到的 stackBlock 已经被回收,会变成野指针。正确的做法是先将 stackBlock 拷贝到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]];如果是在 ARC 中就不用担心此问题,因为它会默认将实例化的 block 拷贝到堆上(后面会详细学习)

block 的内存分布

1 - MRC 环境

// - Student.h

 #import <Foundation/Foundation.h>
 @interface Student : NSObject
 @end

// - Student.m

 #import "Student.h"
 @implementation Student
 @end

// - main.m

复制代码
 1 #import <Foundation/Foundation.h>
 2 #import "Student.h"
 3 int main(int argc, const char * argv[]) {
 4 
 5     // ---------------- 测试一 :block 内部不引用任何外部局部变量,位于全局静态区 --------------------
 6     void (^myBlock01)(void) = ^{
 7         NSLog(@"梦想总是遥不可及");
 8     };
 9     myBlock01();
10     NSLog(@"%@",myBlock01);// __NSGlobalBlock__
11 
12     void (^myBlock02)(void) = ^{
13         Student *stu01 = [[Student alloc] init];
14         NSLog(@"%@",stu01);
15     };
16     myBlock02();
17     NSLog(@"%@",myBlock02);// __NSGlobalBlock__
18 
19     static int c = 12;
20     void (^myBlock10)(void) = ^{
21         NSLog(@"%d",c);
22     };
23     myBlock10();
24     NSLog(@"%@",myBlock10);// __NSGlobalBlock__
25 
26 
27     // -------------------- 测试二:当在 block 内部引用外部变量的时,block 内存由全局静态区移到了栈区 --------------------
28     int a = 10;
29     void (^myBlock03)(void) = ^{
30         NSLog(@"%d",a);
31     };
32     myBlock03();
33     NSLog(@"%@",myBlock03);// __NSStackBlock__
34 
35     Student *stu02 = [[Student alloc] init];
36     void(^myBlock04)(void) = ^(){
37         NSLog(@"%@",stu02);
38     };
39     myBlock04();
40     NSLog(@"%@ ",myBlock04);// __NSStackBlock__
41 
42 
43     // -------------------- 测试三:对全局静态区的 block 进行 copy ,那么 block 依旧是全局静态区 --------------------
44     void(^blockGlo)(void) = ^(){
45         NSLog(@"globalBlock Copy");
46     };
47     NSLog(@"%@",blockGlo);// __NSGlobalBlock__
48     NSLog(@"%@",Block_copy(blockGlo));// __NSGlobalBlock__
49 
50 
51     // -------------------- 测试四:对栈区的 block 进行 copy,那么 block 会从栈区拷贝到了堆区 --------------------
52     int b = 11;
53     void (^myBlock05)(void) = ^{
54         NSLog(@"%d",b);
55     };
56     NSLog(@"%@",myBlock05);// __NSStackBlock__
57     NSLog(@"%@",Block_copy(myBlock05));// __NSMallocBlock__
58     
59 
60     // -------------------- 测试五:当 block 内部引入变量是对象类型时,copy 操作会对该对象的引用计数加 1,这就要注意内存问题了 --------------------
61     Student *stu05 = [[Student alloc] init]; // 1
62     void(^myBlock06)(void) = ^{
63         NSLog(@"%ld",stu05.retainCount); // 因 block 引用了外部对象 stu05,block 则在栈区
64     };
65     myBlock06();            // 这里 stu05 的引用计数是 1
66     Block_copy(myBlock06);  // 经 copy 后 block 转移到了 堆区
67     NSLog(@"%ld",stu05.retainCount);// 引用计数变成了 2
68     
69     /* 解决方案
70         方案 ①:对对象使用 __block 修饰
71         方案 ②:将堆区中的 block 进行 release 操作
72      */
73 
74     Student *stu07 = [[Student alloc] init];
75     // 方案 ①:简单高效
76     // __block Student *stu07 = [[Student alloc] init];
77     void(^myBlock08)(void) = ^{
78         NSLog(@"stu07.retainCount = %ld",stu07.retainCount);
79     };
80     
81     // 进行拷贝操作
82     void(^myBlock09)(void) = Block_copy(myBlock08);
83     myBlock09();
84     // 如果使用了采取了方案 ① 使用 __block 修饰,这里 stu07.retainCount = 1
85     // 否则 stu07.retainCount = 2
86     
87     // 方案 ②
88     Block_release(myBlock09);
89     myBlock09();    // stu07.retainCount = 1
90 
91     return 0;
92 }
复制代码

 

posted on   低头捡石頭  阅读(52)  评论(0编辑  收藏  举报

编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示