OC三种内存管理模式:
1. MRC: ManualRetain-Release手动持有释放,OC本身的引用计数alloc/new/copy/mutablecopy release/autorelease。
2. ARC: Automatic Reference Counting 自动引用计数:
Xcode的ProjectSetting中,设置Objective-C AutomaticReference Counting为YES; Xcode 4.2+,新的Project默认为ARC
3. GC:GarbageCollection 垃圾回收,MAC-OS支持,IOS不支持。
内存管理问题:
没有:未初始化或释放后再使用;不对:赋值和转换后使用越界和释放导致的问题;超出:越界;溢出。
释放:不该释放释放了,该释放没有释放(严格对应,多层结构的隐性内存泄露, 构造函数和虚析构函数,函数返回值,释放的时机)。
IOS要从所有权和对象图的整体角度思考。
创建时候拥有所有权: ios中对于内存管理是基于“对象所有权”来维护的, 通过alloc ,new, copy,mutableCopy开头创建的对象,那么这个对象就是拥有所有权。
手动管理:创建或拷贝(alloc,new,copy,mutablecopy)后引用计数加1, 然后Retain拥有所有权引用计数加1;release引用计数减1,autoRelease引用计数推迟减1, 直到引用计数为0,则NSObject的pool管理类会调用dealloc释放对象。
误区纠正1:dealloc不会使得引用计数从1到0。
误区纠正2:没有调用创建拷贝或者retain函数,只是用指针指向它,那么引用计数不用加1.
OC的内存管理,归根结底要记住一条黄金法则:谁创建谁释放,谁retain谁释放。
当等于0时候系统就会调用dealloc释放,不可以自己调用dealloc。记住要在dealloc中调用[super dealoc]。
实例:
(1)简单表现:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSObject *o = [[NSObject alloc]init]; //retainCount为1
[o retain]; //retainCount为2
[o release]; //retainCount为1
[o autorelease]; //retainCount为1
[pool release]; //retaincount为0,触发dealloc方法
(2)类实例作为数据成员:
比如ClassA类的一个属性对象的Setter方法:
- ( void )setMyArray:(NSMutableArray *)newArray { if (myArray != newArray) { [myArray release]; // 先release myArray = [newArray retain]; // 后赋值,并调用retain } } |
假设这个类的一个实例为'a',调用setMyArray后,我们就可以说a拥有了一个新的myArray实例,也可以说a引用了一个新的myArray实例。其中调用的retain方法,使myArray的retainCount加一,我们需要注意以下两个地方:
setMyarray方法中,在retain之前先release了旧实例一次
(3)类的析构函数中,设置nil不用再release了:
在本实例的dealloc方法中,本应该是要再次release当前实例的,但回头看看参考内存管理准则。它并不合理,对吧。。。多了一次release。这里比较推荐的做法是:
[ myArray setMyArray:nil ];
这样可以巧妙的使当前实例release而不出错(我们可以向nil发送消息?其实它本身就是个整数0),并符合我们的内存管理准则。更主要的是,很简单,你不需要考虑过多的事情。
当等于0时候系统就会调用dealloc释放,不可以自己调用dealloc。记住要在dealloc中调用[super dealoc]。
(4)强弱引用:
另外一个比较容易忽略而又比较经典的问题是实例变量的循环引用,Objective-C为此区分了,其实也相当相当的简单:
1,强引用,上面讲的就是强引用,存在retainCount加一。
2,弱引用,但凡是assign声明并直接用指针赋值实现的被称之为弱引用,不存在retainCount加一的情况。
(5)AutoRelease延迟对象释放:
:OC提供了autorelease方法,就是提供了半自动的内存管理,将对象的release数目N动态维护,当autoReleasePool调用release时,管理的对象会调用N次release,对象的引用计数为0时候,对象才会被销毁,达到延迟销毁的目的。
(0)定义:
NSAutoreleasePool *pool = [[NSAutoreleasePoolalloc] init];
NSObject *o = [[NSObject alloc] init];
//在pool实例dealloc时,release一次此实例,重要的是并不是在此行去release
[o autorelease];
//此时还可以看到我们的o实例还是可用的,并且retainCount为1
NSLog(@ "oretainCount:%d" ,[oretainCount]);
//pool 的 retainCount为0,自动调用其dealloc方法,我们之前备案的小o也将在这里release一次
[pool release];
(1) 程序启动的时候会有一个默认的autoReleasePool
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
(2) 多个Pool情况下,只有栈顶(最后定义的)的pool才起作用。
NSAutoreleasePool *pool1 = [[NSAutoreleasePoolalloc] init];
NSAutoreleasePool*pool2 = [[NSAutoreleasePool alloc] init];
NSAutoreleasePool*pool3 = [[NSAutoreleasePool alloc] init];
NSObject *o = [[NSObject alloc] init]autorelease];
[pool3 release];
[pool2 release];
[pool1 release];
自动内存管理ARC:
Xcode的Project Setting中,设置Objective-C Automatic Reference Counting为YES; Xcode 4.2+,新的Project默认为ARC
ARC优点:
不用写retain/release/autorelease这样的代码,甚至连dealloc方法都不需要(前提是类不需要管理C指针)
更好的性能,ARC智能地在”适当“的地方插入retain/release
Block justwork
Less code,less bug
少浪费脑细胞和脑时钟周期。
ARC实现的前提:
为了实现ARC,LLVM编译器必须保证4条规则
1.不能调用或实现跟引用计数相关的方法(retain/release/autorelease/retaincount等),否则产生编译错误
2.C的结构体里面,不能有Object指针;如果必须使用,那么可以用Objective-C的类代替
3.编译器必须清楚void*是否被retained。引入新的API来转换Objective-C和Core Foundation类型的对象
4.编译器必须清楚自动释放的对象,因此AutoreleasePool不能是对象,而只是语义上的符号。@autoreleasepool directive甚至可能在MRC中使用。
MRC和ARC总结:
笔者其实觉得MRC很容易掌握,profile的工具也容易掌握。但ARC实实在在的从安全、可维护性、代码量、性能上有全面的提升。所以除非维护旧的代码,笔者都建议使用ARC。不用担心app依赖第三方的MRC代码,可以通过编译器设置文件的编译属性来混合使用ARC和MRC。