OC语言 - 内存管理:strong | weak | unsafe_unretained | autoreleasing
前言
1 -对 weak 和 strong 描述最多的就是:它们由 ARC 引入,weak 相当于 MRC 中的 assign,但 weak 是用于修饰对象,两者都不会造成引用计数加 1;strong 相当于 MRC 中规定的 retain,它会造成引用计数加 1
2 - strong 指针能够保持对象的生命,一个对象只要有 strong 指针指向它,那么它就不会被释放;相反如果一个没有 strong 指针指向它,那么它将会被自动释放。默认所有实例变量和局部变量都是 stong 指针
3 - weak 型指针变量虽然可以指向一个对象,但不属于对象的拥有者,就是说当对象被销毁时,这个 weak 指针也就自动指向 nil
strong
1 - 如果硬是要在 MRC 环境下使用 strong,它和 retain 并没有什么区别
@property(strong,nonatomic)NSMutableArray *arrayI;
@property(strong,nonatomic)NSMutableArray *arrayII;
① 测试一:使用点语法置 nil | 直接使成员变量置 nil
1 self.arrayI = [[NSMutableArray alloc] init]; 2 NSLog(@"%ld %ld-----%p %p",self.arrayI.retainCount,_arrayI.retainCount,self.arrayI,_arrayI); 3 // 2 2-----0x10074e660 0x10074e660 4 // _arrayI --> arrayI 5 6 7 self.arrayII = self.arrayI; 8 NSLog(@"%ld %ld-----%p %p",self.arrayII.retainCount,_arrayII.retainCount,self.arrayII,_arrayII); 9 // 3 3-----0x10074e660 0x10074e660 10 // _arrayII --> arrayI 11 12 13 self.arrayI = self.arrayII; 14 NSLog(@"%ld %ld-----%p %p",self.arrayI.retainCount,_arrayI.retainCount,self.arrayI,_arrayI); 15 // 3 3-----0x10074e660 0x10074e660 16 17 18 // // 验证一:直接 release 19 // _arrayI = nil; 20 // NSLog(@"self.arrayI:%ld %ld %p",self.arrayI.count,_arrayI.count,self.arrayI); // 0 0 0x0 21 // // 猜一猜会输出什么 ? 22 // NSLog(@"self.arrayII:%p %ld",_arrayII,_arrayII.retainCount); //0x10074e660 3 23 24 25 // 验证二:使用点语法 26 self.arrayI = nil; // 使用点语法 _arrayI 会先进行 release 一次(这就是下行代码会输出 2 的原因),然后再置 nil 27 NSLog(@"self.arrayI:%ld %ld %p",self.arrayI.count,_arrayI.count,self.arrayI); // 0 0 0x0 28 // 猜一猜又会输出什么 ? 29 NSLog(@"self.arrayII:%p %ld",_arrayII,_arrayII.retainCount); //0x10074e660 2
② 测试二:直接赋值
1 self.arrayI = [[NSMutableArray alloc] init]; 2 3 NSLog(@"self.arrayI:%p",self.arrayI); // 0x10066b830 4 NSLog(@"%ld %ld",self.arrayI.retainCount,_arrayI.retainCount);// 2 2 5 6 7 // 直接赋值 8 _arrayII = self.arrayI; 9 NSLog(@"%ld %ld",self.arrayII.retainCount,_arrayII.retainCount);// 2 2 10 NSLog(@"%p %p",self.arrayI,_arrayII); // 0x10066b830 0x10066b830 11 12 13 self.arrayI = nil; 14 NSLog(@"self.arrayI:%p",self.arrayI);// 0x0 15 NSLog(@"%ld",self.arrayII.retainCount);// 1 16 NSLog(@"self.arrayII:%p",self.arrayII);// 0x10066b830
2 - ARC 环境中的 strong
1 // abcName 作为 @"natsu" 的最初持有者,是 @"natsu" 对象的 Strong reference 2 NSString *abcName = [[NSString alloc] initWithString:@"natsu"]; 3 4 // abcName 赋值给 xyzName,则 xyzName 同样是 @"natsu" 对象的 Strong reference 5 NSString *xyzName = abcName; 6 7 // abcName 重新指向了新的内容 8 abcName = [[NSString alloc] initWithString:@"mark"]; 9 // 此时 abcName 抛弃 @"natsu" 对象,成为 @"mark" 对象的持有者 10 // 而 @"natsu" 对象持有者只剩下 xyzName 11 // 所以,@"natsu" 和 @"mark 两个对象都存在内存之中 12 13 14 // otherName 是 @"mark" 对象的持有者 15 NSString *otherName = abcName; 16 17 // xyzName 是 @"mark" 对象的持有者 18 xyzName = otherName;// @"natsu" 对象已经没有持有者了,系统会自动把它抛弃
weak
1 - MRC 环境中是不能使用 weak 来修饰对象和基本数据类型,该概念是从 iOS 5 / Mac OS X 10.7 开始导入的。weak 修饰的属性一旦释放了内存,其指针将自动被赋值为 nil(有效防止野指针)。它可以防止循环引用,比如 delegate 和 controller 之间,视图控制器通过 strong 指针 self.view 拥有一个 tableview,但是 tableview 的 delegate 和 datasource 都是 weak 指针,指向你的 controller
1 NSString *name = @"小跳蛙"; 2 __weak NSString *newName = name; 3 NSLog(@"%p %p",name,newName);// 0x100001070 0x100001070 4 name = nil;// 小跳蛙 这个对象被释放 5 NSLog(@"%p",newName);// newName 立即指向 nil 6 7 __weak NSString *pleaFrog = [[NSString alloc] initWithFormat:@"快乐蛙"];// pleaFrog 是 weak 指针修饰的,它不持有对象,所以对象创建出来之后就立马被释放
1 // strongName ---> @"natsu" 2 NSString *strongName = [[NSString alloc] initWithString:@"natsu"];3 3 // weakName ---> @"natsu" 4 __weak NSString *weakName = strongName;// weakName 可以拿到 natsu 对象,但并不是其持有者 5 6 // strongName ---> @"mark" 7 strongName = [[NSString alloc] initWithString:@"mark"];// 此时 natsu 对象便没有了持有者,即被抛弃。同时,weakName 置 nil
unsafe_unretained
1 - 在没有引入 weak 之前采用的是 unsafe_unretained。它和 weak 相似,不同点就是当指针指向的对象被释放时,并不会为其指针自动置 nil,也就形成了野指针
注:尽量避免 unsafe_unretained 使用
1 @property(strong,nonatomic)NSMutableArray *arrayI; 2 @property(unsafe_unretained,nonatomic)NSMutableArray *arrayII;
1 self.arrayI = [[NSMutableArray alloc] init]; 2 self.arrayII = self.arrayI; 3 self.arrayI = nil;// 一旦 arrayI 将内存释放掉,但是 self.arrayII 并不知情,依旧保留着原来的内存地址,接下来的事情,就是 crash 4 NSLog(@"self.arryyII:%p",self.arrayII);// 崩溃
autoreleasing
1 - 该关键字使对象延迟释放,相当于 MRC 中的 Autorelease:比如自己想要把一个未初始化的对象引用到另一个方法当中,那么这种情况就可以使用 autoreleasing
1 -(void)performTestI{ 2 NSError *error = nil; 3 [self generateErrorInVariable:&error]; 4 NSLog(@"Error:%@",error);// 这里就可以使用该方法外创建的实例对象,同样适合谁分配谁释放的原则 5 6 } 7 8 -(void)generateErrorInVariable:(__autoreleasing NSError **)paramError{ 9 10 *paramError = [[NSError alloc] initWithDomain:@"myAPP" code:1 userInfo:nil]; 11 12 }
2 - 如果方法的返回值是在自身方法中申请的,那么希望释放是在调用端中进行的话,往往需要在该方法内做以下处理
1 - (NSString *)stringAutorelease{ 2 3 // MRC 4 NSString *strMRC = [[NSString alloc] initWithFormat:@"stringTest"]; 5 return [[strMRC retain] autorelease]; 6 7 // ARC 8 __autoreleasing NSString *strARC = [[NSString alloc] initWithFormat:@"stringTest"]; 9 return strARC; 10 }
ARC 环境下的 strong、weak、unsafe_unretained
1 - 代码示例
// - person.m
1 #import "Person.h" 2 @implementation Person 3 -(void)dealloc{ 4 5 NSLog(@"%@已销毁",self); 6 } 7 8 9 // 作用域一 10 -(void)testOne{ 11 12 13 __weak Person *weakPerson; 14 __unsafe_unretained Person *unsafePerson; 15 __strong Person *strongPerson; 16 17 18 NSLog(@"111111111"); 19 20 // 作用域二 21 { 22 23 // weakPerson 出了作用域二,立即销毁并自动置 nil 24 weakPerson = [Person new];// 0x10062d530 25 26 // unsafePerson 出了作用域二,立即销毁但并不会置 nil,成为野指针 27 unsafePerson = [Person new];// 0x10320c200 28 29 // strongPerson 出了方法(作用域一)后销毁 30 strongPerson = [Person new]; // 0x10052afd0 31 32 } 33 NSLog(@"weakPerson = %@",weakPerson); // (null) 34 NSLog(@"unsafePerson = %@",unsafePerson); // 野指针:依旧指向原地址 0x10320c200 35 NSLog(@"strongPerson = %@",strongPerson); // 0x10052afd0 36 37 NSLog(@"999999999"); 38 39 // weakPerson 和 unsafePerson 出了作用域二即刻销毁,在 NSLog(@"111111111") 和 NSLog(@"999999999") 之间执行 40 // strongPerson 和 main 函数中的 p1,两者的 dealloc 在 NSLog(@"999999999") 执行完毕之后 41 42 } 43 44 @end
// - main.m
1 #import <Foundation/Foundation.h> 2 #import "Person.h" 3 int main(int argc, const char * argv[]) { 4 5 Person *p1 = [Person new];// 0x10301b4f0 6 [p performSelector:@selector(testOne)]; 7 // 执行完 testOne 方法后 p1 自动销毁 8 return 0; 9 10 }
日志信息
小结:ARC 环境中的内存管理
1 - ARC 只能工作于 OC 对象,如果应用了 core foundation 或者 malloc()/free() 还是要自己手动管理内存的
2 - 不能使用 NSAutoreleasePool 对象,要使用 @autoreleasepool 块来代替它
3 - 不能使用内存存储区 NSZone
4 - 声明 IBOutlet 时一般使用 weak,除了对 StoryBoard 这样 nib 中间的顶层对象要用 strong
5 - copy 和之前 MRC 环境下的 copy 一样,有深拷贝、浅拷贝之分
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)