OC语言 - block:内存分布
copy | retain | release
1 - block 在内存中的存储状况分为 NSGlobalBlock、NSStackBlock 和 NSMallocBlock 三种
2 - 对于 block 而言,我们对 block 不管是 retain、copy、release 都不会改变其引用计数:retainCount 始终是 1。block_copy 和 copy 等效;block_release 和 release 等效
② 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 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)