基本内存管理规则
内存管理模型基于对象所有权。任何对象都可以有一个或多个所有者。只要一个对象至少有一个所有者,它就继续存在。如果一个对象没有所有者,运行时系统会自动销毁它。为了确保在拥有对象和不拥有对象时都很清楚,Cocoa设置了以下策略:
- 自己会成为自己创建对象的所有者:
使用名称以“alloc”、“new”、“copy”或“mutableCopy”开头的方法创建对象(例如alloc、newObject或mutableCopy)。 - 您可以使用retain获得对象的所有权:
(1) 在访问器方法或init方法的实现中,获取要存储为属性值的对象的所有权;
(2) 防止对象作为其他操作的副作用而失效 - 当您不再需要它时,您必须放弃您拥有的对象的所有权
调用dealloc - 您不得放弃您不拥有的对象的所有权
不能调用其他对象的dealloc方法
引用计数
引用计数简单来说就是,当我们创建一个新对象的时候,它的引用计数为 1,当有一个新的指针指向这个对象时,引用计数 + 1,当某个指针不再指向这个对象是,引用计数 - 1,当对象的引用计数变为 0 时,我们就将对象销毁,回收内存。
所有权策略是使用引用计数实现的
- 创建对象时,其保留计数为1。
- 向对象发送retain消息时,其retain计数将递增1。
- 向对象发送释放消息时,其保留计数将减少1。
- 向对象发送自动释放消息时,其保留计数在当前自动释放池块的末尾递减1。
- 如果一个对象的保留计数减少到零,它将被释放。
应该没有理由显式地询问对象其保留计数是多少(请参见retainCount)。结果往往是误导性的,因为您可能不知道什么框架对象保留了您感兴趣的对象。在调试内存管理问题时,您应该只关心确保代码遵守所有权规则。
结合官方文档来理解引用计数,以下代码均在MRC环境下测试
(1) 通过alloc/init ,new, copy/mutableCopy直接创建对象,是对象的所有者 retain count 等于 1
NSObject *obj = [NSObject new];// retainCount == 1;
//...
NSObject *obj1 = [[NSObject alloc] init];//retainCount == 1;
//...
NSMutableString *mutStr = [[NSMutableString alloc] init];//retainCount == 1;
(2)调用 retain 返回当前对象,会使当前对象引用计数+1;
NSObject *obj = [NSObject new];//retainCount == 1;
NSObject *newObj = [obj retain];//retainCount == 2;
(3)release 使当前对象引用计数 -1;
[newObj release];retainCount == 1;
(4) 当引用计数为0时,对象被销毁,系统回收内存
[obj release];retainCount == 1;//retainCount == 0;对象销毁
![](https://img.haomeiwen.com/i7827369/89158412c11ac1e9.png)
自动引用计数ARC
ARC全称Automatic Reference Counting是苹果引入的新的内存管理技术,顾名思义即是帮助我们管理对象的引用计数,在此之前的被叫做MRC(手动管理引用计数) ,需要程序员在合适的位置插入retain/release 来管理对象的引用计数;
ARC的工作原理就是编译器在合适时机插入retain,release, autorelease来帮助我们管理计数,原理上和MRC一致,这帮助我们解决了大部分的内存问题(Foundation框架下),还有一部分涉及到core Foundation需要我们手动管理
在ARC环境下,对象的retainCount是没有参考价值的:
ARC环境下要注意一些规则:
- 代码中不能使用 retain / release / autorelease,否则编译报错
- 须遵守内存管理的方法命名规则,
- 不能显式调用 dealloc
- 使用 @autoreleasepool 块替代 NSAutoreleasePool
强引用和弱引用
有两种类型的对象引用:
强引用,使对象在内存中保持“活动”状态。
弱引用,对引用对象的生存期没有影响。
代码中的大多数引用都是强引用,这是使用以下形式声明对象引用时的默认行为:
NSString * someReferenceToAString;
要创建弱引用,请使用__weak限定符,如下所示:
NSString * __weak someWeakReferenceToAString;
循环引用问题
通过引用计数的原理,我们知道当一个对象的引用计数为0的时候系统才会销毁它。假如A对B持有强引用,B也同样对A持有强引用,那么A的销毁需要B先销毁,B的销毁又依赖A,如此一来就形成相互引用
![](https://img.haomeiwen.com/i7827369/75d30a6537296319.png)
解决办法,就是我们在合适的时机主动断开对对另一个的引用:
![](https://img.haomeiwen.com/i7827369/8534284a658d1b3e.png)
又或者,多个对象相互持有,形成一个环状,实际可能会更复杂,环越大越难被发现
![](https://img.haomeiwen.com/i7827369/84c713c931af14a1.png)
__unsafe_unretained
MRC 下通过声明其中一个变量类型为__unsafe_unretained来避免循环引用
weak
ARC下通过声明其中一个变量类型为weak来避免循环引用,它的变体有__weak,功能一样,区别是用在不同位置
具体区别查看属性关键字详解
__unsafe_unretained和weak区别
__unsafe_unretained通常用在MRC环境下, __unsafe_unretained修饰的对象被释放后,指针不会置空,而是变成一个野指针,那么此时如果访问这个对象的话,程序就会Crash
__weak修饰的对象被释放后,指向对象的指针会置空,不会产生野指针
Core Foundation 对象的内存管理
对于底层 Core Foundation 对象,框架提供了相应的 CFRetain 和 CFRelease 方法。类似于Foundation下的retain,release,按照手动管理引用计数的方式管理即可:
Foundation对象是Objective-C对象,使用Objective-C语言实现;而Core Foundation对象是C对象,使用C语言实现。两者之间可以通过__bridge、__bridge_transfer、__bridge_retained等关键字转换(桥接)。
__bridge 不改变当前对象的引用计数
__bridge_transfer 会会使当前对象的引用计数-1;
__bridge_retained 会会使当前对象的引用计数+1;
网友评论