• iOS/OS X内存管理(一):基本概念与原理


      

    基本概念

      引用计数(Reference Count)

      为了解释引用计数,我们做一个类比:员工在办公室使用灯的情景。

    • 第一个人进入办公室时,他需要使用灯,于是开灯,引用计数为1
    • 当另一个人进入办公室时,他也需要灯,引用计数为2;每当多一个人进入办公室时,引用计数加1
    • 当有一个人离开办公室时,引用计数减1,当引用计数为0时,也就是最后一个人离开办公室时,他不再需要使用灯,关灯离开办公室。

      内存管理规则

      从上面员工在办公室使用灯的例子,我们对比一下灯的动作Objective-C对象的动作有什么相似之处:

      

      因为我们是通过引用计数来管理灯,那么我们也可以通过引用计数来管理使用Objective-C对象。

      

      而Objective-C对象的动作对应有哪些方法以及这些方法对引用计数有什么影响?

      

      当你alloc一个对象objc,此时RC=1;在某个地方你又retain这个对象objc,此时RC加1,也就是RC=2;由于调用alloc/retain一次,对应需要调用release一次来释放对象objc,所以你需要release对象objc两次,此时RC=0;而当RC=0时,系统会自动调用dealloc方法释放对象。

      Autorelease Pool

      在开发中,我们常常都会使用到局部变量,局部变量一个特点就是当它超过作用域时,就会自动释放。而autorelease pool跟局部变量类似,当执行代码超过autorelease pool块时,所有放在autorelease pool的对象都会自动调用release。它的工作原理如下:

    • 创建一个NSAutoreleasePool对象
    • 在autorelease pool块的对象调用autorelease方法
    • 释放NSAutoreleasePool对象

      

      iOS 5/OS X Lion前的(等下会介绍引入ARC的写法)实例代码如下:

      

      由于放在autorelease pool的对象并不会马上释放,如果有大量图片数据放在这里的话,将会导致内存不足。

    ARC管理方法

      iOS/OS X内存管理方法有两种:手动引用计数(Manual Reference Counting)和自动引用计数(Automatic Reference Counting)。从OS X Lion和iOS 5开始,不再需要程序员手动调用retainrelease方法来管理Objective-C对象的内存,而是引入一种新的内存管理机制Automatic Reference Counting(ARC),简单来说,它让编译器来代替程序员来自动加入retainrelease方法来持有和放弃对象的所有权。

      在ARC内存管理机制中,id和其他对象类型变量必须是以下四个ownership qualifiers其中一个来修饰:

    • __strong(默认,如果不指定其他,编译器就默认加入)
    • __weak
    • __unsafe_unretained
    • __autoreleasing

      所以在管理Objective-C对象内存的时候,你必须选择其中一个,下面会用一些列子来逐个解释它们的含义以及如何选择它们。

      __strong ownership qualifier

      如果我想创建一个字符串,使用完之后将它释放调用,使用MRC管理内存的写法应该是这样:

      而如果是使用ARC方式的话,就text对象无需调用release方法,而是当text变量超过作用域时,编译器来自动加入[text release]方法来释放内存

      而当你将text赋值给其他变量anotherText时,MRC需要retain一下来持有所有权,当textanotherText使用完之后,各个调用release方法来释放。

      

      而使用ARC的话,并不需要调用retainrelease方法来持有跟释放对象。

      除了当__strong变量超过作用域时,编译器会自动加入release语句来释放内存,如果你将__strong变量重新赋给它其他值,那么编译器也会自动加入release语句来释放变量指向之前的对象。例如:

      前面已经提过内存管理的四条规则

      

      我们总结一下编译器是按以下方法来实现的:

    • 对于规则1和规则2,是通过__strong变量来实现,
    • 对于规则3来说,当变量超过它的作用域或被赋值或成员变量被丢弃时就能实现
    • 对于规则4,当RC=0时,系统就会自动调用

      __weak ownership qualifier

      其实编译器根据__strong修饰符来管理对象内存。但是__strong并不能解决引用循环(Reference Cycle)问题:对象A持有对象B,反过来,对象B持有对象A;这样会导致不能释放内存造成内存泄露问题。

      

      如何解决?于是我们引用一个__weakownership qualifier,被它修饰的变量都不持有对象的所有权,而且当变量指向的对象的RC为0时,变量设置为nil。例如:

      

      由于text变量被__weak修饰,text并不持有@"Sam Lau"对象的所有权,@"Sam Lau"对象一创建就马上被释放,并且编译器给出警告⚠️,所以打印结果为(null)

    所以,针对刚才的引用循环问题,只需要将Test类的属性objc设置weak修饰符,那么就能解决。

      

      __unsafe_unretained ownership qualifier

      __unsafe_unretained ownership qualifier,正如名字所示,它是不安全的。它跟__weak相似,被它修饰的变量都不持有对象的所有权,但当变量指向的对象的RC为0时,变量并不设置为nil,而是继续保存对象的地址;这样的话,对象有可能已经释放,但继续访问,就会造成非法访问(Invalid Access)。例子如下:

      __autoreleasing ownership qualifier

      引入ARC之后,让我们看看autorelease pool有哪些变化。没有ARC之前的写法如下:

      但是我们很少或基本上不使用autorelease pool。当我们使用XCode创建工程后,有一个app的入口文件main.m使用了它

     

      Property(属性)

      有了ARC之后,新的property modifier也被引入到Objective-C类的property,例如:

      

    总结

      要想掌握iOS/OS X的内存管理,首先要深入理解引用计数(Reference Count)这个概念以及内存管理的规则;在没引入ARC之前,我们都是通过retainrelease方法来手动管理内存,但引入ARC之后,我们可以借助编译器来帮忙自动调用retainrelease方法来简化内存管理和减低出错的可能性。虽然__strong修饰符能够执行大多数内存管理,但它不能解决引用循环(Reference Cycle)问题,于是又引入另一个修饰符__weak。被__strong修饰的变量都持有对象的所有权,而被__weak修饰的变量并不持有对象所有权。下篇我们介绍使用工具如何解决常见内存问题:悬挂指针和内存泄露

  • 相关阅读:
    selenium之css选择器高级用法
    常系数线性齐次递推新理解
    关于莫队本质的理解
    2021.5.8总结
    决策单调性优化dp
    字符串 复习
    5.1总结
    树分块 学习笔记
    莫反 复习
    P4570 [BJWC2011]元素
  • 原文地址:https://www.cnblogs.com/ChouDanDan/p/5210270.html
Copyright © 2020-2023  润新知