戴远的IT博客 首页 阅读

理解objective-c内存管理

引用计数

对象的创建是一件消耗资源的事情,所以,在对象使用后,要将对象及时从内存中释放掉。 为了管理对象的创建和释放,在iOS中,我们引入了“引用计数”这一技术。

下面我们通过一个生活中的例子,来形象的说明引用计数是怎么一回事。

我们可以把房间里的灯看做对象,灯亮着,表示该对象正在被使用;灯灭了,表示该对象被释放掉了。 当有人进入房间里,打开灯,离开屋子,关掉灯。好,规则就是这样,现在来它是怎么运作的。

第一个人进入房间,“需要照明的人数”是1,此时引用计数值是从0变成了1,因此要开灯,相当于要创建对象。

第二个人进入房间,“需要照明的人数”是2,此时引用计数值是从1变成了2,由于灯已开,相当于持有对象。

有一个人离开房间,“需要照明的人数”变成1,此时引用计数值是从2变成了1。 由于对象的引用计数不为0,所以不能关灯(不能释放对象),如果此时关灯,会影响到在屋子里的另外一个人。

最后一个人离开房间,“需要照明的人数”变成0,此时引用计数值是从1变成了0。需要关灯,相当于释放了对象。

通过这个例子,我们可以很直观的知道,引用计数其实就是对象被多少人使用。 如果对象在被使用,则不能释放,如果没有人使用,则对象会被释放掉。

内存管理的原则

在我们实际写程序时,会遵循下面的几个原则:

相对应的objective-c方法:

自己生成的对象,自己持有

用new/alloc/copy/mutableCopy开头的方法名创建的对象,意味着自己持有该对象。

这里有一个编写方法的默认约定,如果以上面4个关键字开头命名的方法,返回的对象会自动持有,应该这样写:


- (id)allocObject {
  id obj = [[NSObject alloc] init];
  return obj;
}

// 创建对象,并自动持有
id a = [XX allocObject];

// 使用完毕后,释放对象
[a release];

如果不是以上面4个关键字作为开头命名的方法,返回的对象不会被自动持有,应该这样写:


- (id)createObject {
  id obj = [[NSObject alloc] init];

  // 对象调用autorelease方法,放入自动释放池。
  // 关于autorelease,稍后会提及到。
  return [obj autorelease];
}

// 创建对象,由于是autorelease的对象,所以,不持有该对象
id a = [XX createObject];

// 保持对象
[a retain];

// 释放对象
[a release];

非自己生成的对象,也可以持有;不再持有的对象,要释放掉

// 由于不是由alloc/new/copy/mutableCopy开头的方法名创建的对象,自己不持有该对象
id obj = [NSMutableArray array];

// 所以,要持有该对象
[obj retain];

// 使用该对象
[obj ...];

// 使用完毕,释放对象
[obj release];

不是自己持有的对象,不能释放

// 由于不是由alloc/new/copy/mutableCopy开头的方法名创建的对象,自己不持有该对象
id obj = [NSMutableArray array];

// 没有持有该对象,释放对象导致程序崩溃
[obj release];  

autorelease

autorelease能够在对象超出生命周期之后,能正确自动的释放对象。 调用release会让对象的引用计数减1,如果计数值减到0,则对象会立即释放。 但autorelease则不然,对象不会被立即释放,调用autorelease会将对象注册到自动释放池中,等到自动释放 池被pool时,所有池中的对象都会被调用release方法,进行释放。 所以,可以看出,release与autorelease的不同之处就在于,一个是立即释放,一个则不是。

autoreleasepool

说到自动释放,就不能不提自动释放池–autoreleasepool。 autoreleasepool可以看做变量的作用域,autorelase的对象,超出这个范围,就会被释放。


NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];

ARC

ARC是一种编译器技术,将以前人工持有、释放对象的过程,交由编译器来做。减小了内存泄露和对象过度释放的情况的发生, 极大地提高了开发效率。

ARC的原则

ARC下,4种对象修饰符

__strong 强引用

__strong是默认的修饰符,例如我们定义的对象,即使不指明__strong,也默认是__strong修饰符来修饰的。

id obj = [NSObject new];

// 等价于
id __strong obj = [NSObject new];

__weak 弱引用

__weak是为了解决对象之间循环引用而引入的修饰符。用__weak修饰的对象,只是简单的将指针指向对象,但引用计数不加1。 但对象被释放后,指针将被指向nil。

__ unsafe_unretained

作用同__weak,但适用于在iOS SDK4.0之前。与__weak的区别在于,如果对象被释放,则指针会变成野指针。

__autoreleasing

在ARC中,不能使用autorelease关键字,但autorelease功能是起作用的。所以,需要使用__autorelieasing修饰符。

@autoreleasepool {
  id __autoreleasing obj = [[NSObject alloc] init];
}

--EOF--
若无特别说明,本站文章均为原创,转载请保留[链接]