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 一样,有深拷贝、浅拷贝之分

 

posted on   低头捡石頭  阅读(79)  评论(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

导航

统计

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