内存分配
在iOS中,数据是存储在堆和栈上的,堆上的内存需要管理,栈上的内存并不需要。
-
非OC对象(基础数据类型)存储在栈上
int
,double
,float
,NSInteger
,CGFloat
,BOOL
等
-
OC对象存储在堆上
NSString
,NSArray
,NSDictionary
等
引用计数
-
MRC引用计数
对象操作的四个类别
对象操作 | OC中对应的方法 | 对应的 retainCount 变化 |
---|---|---|
生成并持有对象 | alloc/new/copy/mutableCopy等 | +1 |
持有对象 | retain | +1 |
释放对象 | release | -1 |
废弃对象 | dealloc | - |
四个法则
- 自己生成的对象,自己持有。
- 非自己生成的对象,自己也能持有。
- 不在需要自己持有对象的时候,释放。
- 非自己持有的对象无需释放。
如下是四个黄金法则对应的代码示例:
/*
* 自己生成并持有该对象
*/
id obj0 = [[NSObeject alloc] init];
id obj1 = [NSObeject new];
/*
* 不在需要自己持有的对象的时候,释放
*/
id obj = [[NSObeject alloc] init]; // 此时持有对象
[obj release]; // 释放对象
/*
* 指向对象的指针仍就被保留在obj这个变量中
* 但对象已经释放,不可访问
*/
/*
* 非自己持有的对象无法释放
*/
id obj = [NSArray array]; // 非自己生成的对象,且该对象存在,但自己不持有
[obj release]; // 此时将运行时crash 或编译器报error。 非ARC下,调用该方法会导致编译器报 issues。此操作的行为是未定义的,可能会导致运行时 crash 或者其它未知行为
其中 非自己生成的对象,且该对象存在,但自己不持有 这个特性是使用autorelease
来实现的,示例代码如下:
- (id) getAObjNotRetain {
id obj = [[NSObject alloc] init]; // 自己持有对象
[obj autorelease]; // 取得的对象存在,但自己不持有该对象
return obj;
}
autorelease
使得对象在超出生命周期后能正确的被释放(通过调用release
方法)。在调用release
后,对象会被立即释放,而调用autorelease
后,对象不会被立即释放,而是注册到 autoreleasepool
中,经过一段时间后 pool
结束,此时调用release
方法,对象被释放。
在MRC的内存管理模式下,与对变量的管理相关的方法有:retain
, release
和 autorelease
。retain
和 release
方法操作的是引用记数,当引用记数为零时,便自动释放内存。并且可以用 NSAutoreleasePool
对象,对加入自动释放池(autorelease
调用)的变量进行管理,当 drain
时回收内存。
-
ARC引用计数
ARC 是苹果引入的一种自动内存管理机制,会根据引用计数自动监视对象的生存周期,实现方式是在编译时期自动在已有代码中插入合适的内存管理代码以及在 Runtime 做一些优化。ARC其实也是基于引用计数,只是编译器在编译时期自动在已有代码中插入合适的内存管理代码(包括 retain
、release
、copy
、autorelease
、autoreleasepool
)以及在 Runtime 做一些优化。
现在的iOS开发基本都是基于ARC的,所以开发人员大部分情况都是不需要考虑内存管理的,因为编译器已经帮你做了。为什么说是大部分呢,因为底层的 Core Foundation 对象由于不在 ARC 的管理下,所以需要自己维护这些对象的引用计数。
还有就算循环引起情况就算由于互相之间强引用,引用计数永远不会减到0,所以需要自己主动断开循环引用,使引用计数能够减少。