OC的内存管理分为ARC和MRR两种机制,前者是系统自动去管理内存的,后者是我们主动去管理内存的。在这里我们主要讨论的后者。
引用计数 retainCount
无论是ARC还是MRR都是通过OC的引用计数机制来管理内存的(也就是retainCount) ,每当一个对象被创建起来时,它的引用计数就加一,当某个对象的引用计数为0时,说明这个对象就没有被使用了,也就是说这个对象的拥有者就为0了,这时编译器就会摧毁这个对象。当外部需要使用这个对象时,一般都是通过传递参数的形式来传递这个对象,此时这个对象在传递时应声明为strong或者是retain来表示对这个对象的拥有权,此时retainCount会加一。如果外部不在使用这个对象了,就使用release来将外部对这个对象的引用释放掉,此时retainCount减一。当retainCount等于0时,编译器就会摧毁掉这个对象。
内存管理的两种错误
1.由于没有声明为strong对使用对象的强引用,使用对象被直接释放掉了,或者重写了对象。
2.没有释放不再使用的对象,而造成内存泄漏。
对象的拥有权(什么时候该释放一个对象)
1.自己创建的对象 就有拥有权,通过alloc copy mutablecopy new创建的对象就有拥有权,实际上,只要见到这几个关键字,就应该要想到要释放对象。
@interface Test : NSObject
@property (nonatomic, strong) NSString *address;
@property (nonatomic, assign) int age;//C语言的基本数据类型
@end
@implementation Test
- (void)dealloc{
[self.address release];//只需要在非ARC模式下手动释放属性变量,ARC模式下系统会自动释放
[super dealloc];//只有在非ARC模式下才可用super dealloc,不然会报错
}
- (NSString *)test1{
NSString *name = [[NSString alloc] init];
[name autorelease];//延迟释放
return name;
}
- (void)test2{
NSString *temp = [[self test1] retain];
NSLog(@"%@", temp);
[temp release];
}
@end
在Test类的方法test1实现中,我们创建了一个oc字符串对象name,并且调用了init方法进行了初始化,但是这个对象的生命周期仅在当前代码块中。由于是MRR模式,我们需要手动释放这个对象所占的内存,但是我们又要返回一个字符串对象,因此我们调用了一个autorelease方法,来延迟释放name。autorelease的工作机制就是把调用了这个方法的对象放入自动释放池中,然后系统每隔一段时间去检查自动释放池中该对象的引用计数,如果该对象的引用计数为0了,那么系统就会摧毁掉这个对象。如果不释放对象就会造成内存泄漏的问题。
在方法test2的实现中,我们又创建了一个OC的临时对象--temp,虽然我们并没有看到任何的以上所说的标志性的关键字,但是我们看到在 test1 方法的返回值的右边我们用了一个属性关键字--retain (可以使用retain声明对一个不是自己创建的对象的拥有权) ,这个关键字说明了这个temp对 test1 返回对象的拥有权,换句话说,现在test1 返回对象的引用 计数就是 2 了,所以尽管没有看到那些关键字,我们还是要在这个代码块结束之前释放掉temp对那个对象的拥有权,因此在 [temp release] 后,那个对象的引用计数就是 1 (2-1)了。
使用dealloc去释放对象自己拥有的资源
我们需要重写NSObject的dealloc方法,但是我们不会主动去调用某个对象的dealloc方法,这个方法是系统自己主动调用的。当一个对象的retainCount = 0 系统就会摧毁这个对象,但是在释放这个对象之前 系统会去这个对象里面查找是否实现了dealloc方法,如果实现了,就主动调用然后将这个对象释放掉。
释放对象的步骤:
1. 调用对象的dealloc方法
1.释放掉自己拥有的资源
2.调用super dealloc
2. 释放这个对象
@interface Test : NSObject
@property (nonatomic, strong) NSString *address;
@property (nonatomic, assign) int age;
@end
@implementation Test
- (void)dealloc{
[self.address release];//由于address属性变量是strong的,所以需要release掉,但是age不是,所以不用release掉
[super dealloc];
}
- (NSString *)test1{
NSString *name = [[NSString alloc] init];
[name autorelease];//延迟释放
return name;
}
- (void)test2{
NSString *temp = [[self test1] retain];
NSLog(@"%@", temp);
[temp release];
}
@end
还是上面的Test类的实现,我们看到dealloc的具体实现,但却没有看见它的声明,因为这个方法是系统自己调用的,我们是不会主动去调用这个方法的。在dealloc方法中,我们看到了对当前对象的address调用了release方法,由于这里我们确定是在这里此刻释放掉address了(dealloc方法里),所以我们没有使用autorelease,由于age是C语言基本类型定义的对象,所以我们不用主动释放掉它,之后我们调用父类的dealloc方法来释放掉子类的这个对象。由于当前的Test对象是无法自己释放自己的,由他的初始化就可以看出来,它是由他的父类的init方法来初始化的(我们没有重写它的父类的方法),所以释放也还是由让他的父类的dealloc方法来释放掉Test对象。
#import <Foundation/Foundation.h>
#import "Test.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *add = [[[NSString alloc] initWithString:@"rc"] autorelease];
//retainCount 1
Test *t = [Test new];
t.address = add;//retainCount 2
[t.address length];
}
return 0;
}
我们到主程序中来看看,我们创建了一个OC的字符串对象 add ,并且自定义了它的init方法,然后我们为了防止后面忘记释放这个对象,所以我们就把他暂时放到了自动释放池中,然后我们创建了一个Test 对象 t ,然后调用了 t 中的address属性变量的set方法,由于address是strong的,所以引用计数要加一。