一起学Objective-C - 内存管理

来源:互联网 发布:火绒安全软件设置 编辑:程序博客网 时间:2024/05/22 14:52

Objective-C的内存管理要比Java复杂多了,很有挑战性,它提供了三种方法:

-  Explicit

有可以通过alloc, copy等方法为对象分配内存,dealloc销毁已分配的内存。这种方法你可以对内存管理有完全的控制力,高性能但是出错的可能也大。

- Retain count

利用OpenStep的retain/release机制,同时使用autorelease pools来提供一定程度的自动化内存管理,这种方法提供了比较好的对内存管理的控制,但是需要仔细的遵循一些简单的规则,这种方法效率也很高。

- Garbage collection

类似于Java的内存回收机制,但是比起Java来就差远了,还有一定使用限制,iOS上是不支持的,所以暂时先不研究了。

话说虽然是三个方法,但是主要还是使用第二种。建议使用NSObject.h里面的一些标准的macro来包装etain/release/autorelease机制。

 

标准的OpenStep系统利用retain count来进行内存管理,当一个对象被创建时,它的retain count是1. 当对象被retain时,这个retain count增加,当对象被release时,这个retain count减少,当retain count减少到零时,对象就被销毁了。

 Coin*c = [[Coin alloc] initWithValue: 10];

 

    // Put coin in pouch,

  [c retain];// Calls 'retain' method (retain count now 2)

    // Remove coin from pouch

  [c release];// Calls 'release' method (retain count now 1)

    // Drop in bottomless well

  [c release];// Calls 'release' ... (retain count 0) then 'dealloc'

如果一个代码块中一个对象被创建了,它就有义务release掉这个对象,如果一个代码块只是简单使用某个对象,那它就没义务release这个对象。简单的说,总的retain数应该等于总的releasee数。

  NSString *msg = [[NSString alloc] initWithString: @"Test message."];

  NSLog(msg);

    // we created msg with alloc -- release it

  [msg release];

Retain和release必须被用来操作对象的实例:

- (void)setFoo:(FooClass *newFoo)

{

    // first, assert reference to newFoo 保证这期间,该对象不被销毁

  [newFoo retain];

    // now release reference to foo (do second since maybe newFoo == foo)

  [foo release];

    // finally make the new assignment; old foo was released and may

    // be destroyed if retain count has reached 0

  foo = newFoo;

}

因为有retain/release机制, 你甚至可以在类中使用setter方法:

- (void)resetFoo

{

  FooClass *foo = [[FooClass alloc] init];

  [self setFoo: foo];

    // since -setFoo just retained, we can and should

    // undo the retain done by alloc

  [foo release];

}

 

例外

有些情况下前面说的关于在代码块中成对使用retain和release的规则并不适用,比方说实现一个container类retain了所有加入到这个container中的对象,同时在这个container被销毁时(在某个方法中),销毁所有它包含的对象。总的来说你要注意成对使用retain和release。

 

Autorelease Pools

有一种情况,当一个对象被转移给另一个对象时,你不想在转移的代码里加retain,但是你又不想让这个对象在完成转移前被销毁掉。OpenStep/GNUstep的解决方案是在这种情况下使用autorelease pool. 一个autorelease pool是一种特殊的机制,会retain对象一定的时间,当然这个时间肯定足够完成对象转移的。实现方法是调用autorelease而不是release. Autorelease首先会把这个对象放到活动的autorelease pool中(这里retain这个对象),然后再给这个对象发一个release消息,等过一段时间之后,autorelease pool会发第二个release消息

 

- (NSString *) getStatus

{

  NSString *status =

    [[NSString alloc] initWithFormat: "Count is %d", [self getCount]];

   // set to be released sometime in the future

  [status autorelease];

  return status;

}

 

如果只在本地使用的话,任何调用-getStatus的代码都可以不需要retain这个方法的返回对象。当然,如果返回需要被保存并且以后使用的话,那就需要retain了。

 

  ...

  NSString *status = [foo getStatus];

    // 'status' is still being retained by the autorelease pool

  NSLog(status);

  return;

    // status will be released automatically later

 

  ...

  currentStatus = [foo getStatus];

    // currentStatus is an instance variable; we do not want its value

    // to be destroyed when the autorelease pool cleans up, so we

    // retain it ourselves

  [currentStatus retain];

 

Pool Management

一个autorelease pool在GNUstep GUI类中会被自动创建,然而如果你只是使用GNUstep Base类的话你就需要自己来创建了。

  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

一旦一个pool被创建之后,所有的autorelease会自动找到它。如果需要关掉这个pool并release它包含的所有的对象的话,只需要release掉pool就可以。

有可能你还会需要创建多个additional pool,那样的话autorelease会找最近创建的进行保存。

autorelease要比release慢很多,所以只在需要用的时候使用它。

 

避免循环Retain

原创粉丝点击