iOS-内存管理

240 阅读4分钟

本文由组内丁武龙分享

前言

在iOS开发中,内存管理是个非常重要的概念,掌握好内存管理,能够让开发者写出内存使用高效率的而且没BUG的代码。

什么是引用计数

引用计数是一个简单而有效的管理对象生命周期的方式,当我们创建一个新对象的时候,其引用计数为1,当有一个新的指针指向这个对象的时候,引用计数就会+1,当指针不再只想这个对象的时候,引用计数-1,当引用计数为0的时候就说明这个对象不再被任何指针指向了,这时对象可以被销毁,回收内存。如下图

为了更直观点,直接代码演示,新建一个工程,由于现在的工程默认都开启了ARC,所以我们需要先修改工程设置,给ViewController.m文件加上-fno-objc-arc的编译参数开启手动引用计数,

在ViewController.m文件中,

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    NSObject *obj = [[NSObject alloc] init];
    NSLog(@"obj retainCount = %ld",[obj retainCount]);
    NSObject *obj2 = [obj retain];
    NSLog(@"obj retainCount == %ld",[obj retainCount]);
    [obj2 release];
    NSLog(@"obj retainCount == %ld",[obj retainCount]);
    [obj release];

}

运行,可以看到控制台输出

 demo[3166:55501] obj retainCount = 1
 demo[3166:55501] obj retainCount == 2
 demo[3166:55501] obj retainCount == 1

问题

如果测试对象释放时,其retainCount是否变成0,看看会出现什么情况

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    NSObject *obj = [[NSObject alloc] init];
    NSLog(@"obj retainCount = %ld",[obj retainCount]);
    NSObject *obj2 = [obj retain];
    NSLog(@"obj retainCount == %ld",[obj retainCount]);
    [obj2 release];
    NSLog(@"obj retainCount == %ld",[obj retainCount]);
    [obj release];
    NSLog(@"obj2 retainCount == %ld",[obj retainCount]);


}


可以看到运行到NSLog(@"obj2 retainCount == %ld",[obj retainCount]);的时候,程序崩溃,这是因为该对象的内存已经被回收了,而我们向一个已经被回收的对象发送retainCount消息,所以它的输出结果应该是不确定的,如果该对象所占的内存被复用了,那么就有可能造成程序异常崩溃。

ARC下的内存管理

ARC能够解决iOS开发中大部分内存管理问题,但是还有少部分内存管理需要开发者自己处理,比如与底层对象交互不在ARC的管理下,所以就需要开发者自己维护这些对象的引用计数
对于常常使用ARC的开发者来说,由于不了解引用计数,经常会出现以下集中问题:

  • 循环引用

循环引用问题

引用计数管理内存的方式虽然很简单,但是也不能很好的解决循环引用的问题,假如有个这样的场景,对象a和对象b,相互引用了对方作为自己的成员变量,只有自己销毁时,成员变量引用计数才会减1,因为对象a的销毁依赖于对象b的销毁,而对象b的销毁又依赖于对象a的销毁,这两个对象即使在外界已经没有任何指针能够访问到它们了,它们也无法被释放,这样就造成了循环引用

解决循环引用

  • 主动断开循环引用
  • 使用弱引用
  • 使用Instruments检测

1.主动断开

在我们明确知道哪里会存在循环引用,在合理的位置主动断开环中的一个引用,使对象得以回收,主动断开循环引用常用语各种block相关的代码逻辑中,不过循环引用这种操作太依赖程序员自己手工控制,这就像回到了 “谁申请谁释放” 的内存管理,因为它依赖于程序员自己是否能发现循环引用并知道什么时候断开循环引用回收内存,所以不太常用

2.使用弱引用

弱引用虽然持有对象,但是不增加引用计数,这样就避免了循环引用的产生,例如我们通常用的delegate模式。两个控制器A、B ,控制器A需要弹出B,让用户输入一内容,当用户输入完成后,B需要将内容返回给A,这个时候,控制器的delegate成员变量通常是一个弱引用,避免两个控制器相互引用对方造成循环引用。

总结

有了ARC的帮助,iOS开发者的内存管理工作已经大大减轻,但是还是需要去理解内存管理方式的有点和遇到的问题(循环引用问题),对于循环引用可以主动断开循环引用也可以使用弱引用的方式去避免造成循环引用。