在Objective-C的内存管理中,其实就是引用计数(reference count)
的管理。内存管理就是在程序需要时程序员分配一段内存空间,而当使用完之后将它释放。如果程序员对内存资源使用不当,有时不仅会造成内存资源浪费,甚至会导致程序crach。我们将会从引用计数和内存管理规则等基本概念开始,然后讲述有哪些内存管理方法,最后注意有哪些常见内存问题。
memory management from apple document
基本概念
引用计数(Reference Count)
为了解释引用计数,我们做一个类比:员工在办公室使用灯的情景。
引用Pro Multithreading and Memory Management for iOS and OS X的图
- 当第一个人进入办公室时,他需要使用灯,于是开灯,引用计数为1
- 当另一个人进入办公室时,他也需要灯,引用计数为2;每当多一个人进入办公室时,引用计数加1
- 当有一个人离开办公室时,引用计数减1,当引用计数为0时,也就是最后一个人离开办公室时,他不再需要使用灯,关灯离开办公室。
内存管理规则
从上面员工在办公室使用灯的例子,我们对比一下灯的动作与Objective-C对象的动作有什么相似之处:
灯的动作 | Objective-C对象的动作 |
---|---|
开灯 | 创建一个对象并获取它的所有权(ownership) |
使用灯 | 获取对象的所有权 |
不使用灯 | 放弃对象的所有权 |
关灯 | 释放对象 |
因为我们是通过引用计数来管理灯,那么我们也可以通过引用计数来管理使用Objective-C对象。
引用Pro Multithreading and Memory Management for iOS and OS X的图
而Objective-C对象的动作对应有哪些方法以及这些方法对引用计数有什么影响?
Objective-C对象的动作 | Objective-C对象的方法 |
---|---|
1. 创建一个对象并获取它的所有权 | alloc/new/copy/mutableCopy (RC = 1) |
2. 获取对象的所有权 | retain (RC + 1) |
3. 放弃对象的所有权 | release (RC – 1) |
4. 释放对象 | dealloc (RC = 0 ,此时会调用该方法) |
当你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
对象
引用Pro Multithreading and Memory Management for iOS and OS X的图
iOS 5/OS X Lion前的(等下会介绍引入ARC的写法)实例代码如下:
1 2 3 4 5 6 7 8 9 |
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // put object into pool id obj = [[NSObject alloc] init]; [obj autorelease]; [pool drain]; /* 超过autorelease pool作用域范围时,obj会自动调用release方法 */ |
由于放在autorelease pool的对象并不会马上释放,如果有大量图片数据放在这里的话,将会导致内存不足。
1 |
for (int i = 0; i |
ARC管理方法
iOS/OS X内存管理方法有两种:手动引用计数(Manual Reference Counting)和自动引用计数(Automatic Reference Counting)。从OS X Lion和iOS 5开始,不再需要程序员手动调用retain
和release
方法来管理Objective-C对象的内存,而是引入一种新的内存管理机制Automatic Reference Counting(ARC),简单来说,它让编译器来代替程序员来自动加入retain
和release
方法来持有和放弃对象的所有权。
在ARC内存管理机制中,id
和其他对象类型变量必须是以下四个ownership qualifiers其中一个来修饰:
- __strong(默认,如果不指定其他,编译器就默认加入)
- __weak
- __unsafe_unretained
- __autoreleasing
所以在管理Objective-C对象内存的时候,你必须选择其中一个,下面会用一些列子来逐个解释它们的含义以及如何选择它们。
__strong ownership qualifier
如果我想创建一个字符串,使用完之后将它释放调用,使用MRC管理内存的写法应该是这样:
1 2 3 4 5 |
{ NSString *text = @"Hello, world"; //@"Hello, world"对象的RC=1 NSLog(@"%@", text); [text release]; //@"Hello, world"对象的RC=0 } |
而如果是使用ARC方式的话,就text
对象无需调用release
方法,而是当text
变量超过作用域时,编译器来自动加入[text release]
方法来释放内存
1 2 3 4 5 6 7 |
{ NSString *text = @"Hello, world"; //@"Hello, world"对象的RC=1 NSLog(@"%@", text); } /* * 当text超过作用域时,@"Hello, world"对象会自动释放,RC=0 */ |
而当你将text
赋值给其他变量anotherText
时,MRC需要retain
一下来持有所有权,当text
和anotherText
使用完之后,各个调用release
方法来释放。
1 2 3 4 5 6 7 8 9 10 11 |
{ NSString *text = @"Hello, world"; //@"Hello, world"对象的RC=1 NSLog(@"%@", text); NSString *anotherText = text; //@"Hello, world"对象的RC=1 [anotherText retain]; //@"Hello, world"对象的RC=2 NSLog(@"%@", anotherText); [text release]; //@"Hello, world"对象的RC=1 [anotherText release]; //@"Hello, world"对象的RC=0 } |
而使用ARC的话,并不需要调用retain
和release
方法来持有跟释放对象。
1 2 3 4 5 6 7 8 9 10 |
{ NSString *text = @"Hello, world"; //@"Hello, world"对象的RC=1 NSLog(@"%@", text); 本概念开始,然后讲述有哪些内存管理方法,最后注意有哪些常见内存问题。
基本概念引用计数(Reference Count)为了解释引用计数,我们做一个类比:员工在办公室使用灯的情景。
内存管理规则从上面员工在办公室使用灯的例子,我们对比一下灯的动作与Objective-C对象的动作有什么相似之处:
因为我们是通过引用计数来管理灯,那么我们也可以通过引用计数来管理使用Objective-C对象。
而Objective-C对象的动作对应有哪些方法以及这些方法对引用计数有什么影响?
当你 Autorelease Pool在开发中,我们常常都会使用到局部变量,局部变量一个特点就是当它超过作用域时,就会自动释放。而autorelease pool跟局部变量类似,当执行代码超过autorelease pool块时,所有放在autorelease pool的对象都会自动调用
iOS 5/OS X Lion前的(等下会介绍引入ARC的写法)实例代码如下:
由于放在autorelease pool的对象并不会马上释放,如果有大量图片数据放在这里的话,将会导致内存不足。
ARC管理方法iOS/OS X内存管理方法有两种:手动引用计数(Manual Reference Counting)和自动引用计数(Automatic Reference Counting)。从OS X Lion和iOS 5开始,不再需要程序员手动调用 在ARC内存管理机制中,
所以在管理Objective-C对象内存的时候,你必须选择其中一个,下面会用一些列子来逐个解释它们的含义以及如何选择它们。 __strong ownership qualifier如果我想创建一个字符串,使用完之后将它释放调用,使用MRC管理内存的写法应该是这样:
而如果是使用ARC方式的话,就
而当你将
而使用ARC的话,并不需要调用
|