Objective-C 内存管理之autorelease

646 阅读2分钟

autorelease的使用方法如下:

  1. 生成并持有NSAutoreleasePool对象;
  2. 调用已分配对象的autorelease实例方法;
  3. 废弃NSAutoreleasePool对象。

NSAutoreleasePool对象的生命周期相当于c语言变量的作用域。对于所有调用过autorelease实例方法的对象,在废弃NSAutoreleasePool对象是,都自动调用release实例方法。

代码如下:

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

上述代码中的最后一行等同于[obj release]

以GNUstep开源代码为例来看实现原理

static id autorelease_class = nil;
static SEL autorelease_sel;
static IMP autorelease_imp;


 /* Set up the autorelease system ... we must do this before using any
  * other class whose +initialize might autorelease something.
  */
 autorelease_class = [NSAutoreleasePool class];
 autorelease_sel = @selector(addObject:);
 autorelease_imp = [autorelease_class methodForSelector: autorelease_sel];


/**
 * Adds the receiver to the current autorelease pool, so that it will be
 * sent a -release message when the pool is destroyed.<br />
 * Returns the receiver.<br />
 * In GNUstep, the [NSObject+enableDoubleReleaseCheck:] method may be used
 * to turn on checking for retain/release errors in this method.
 */
- (id) autorelease
{
  if (double_release_check_enabled)
    {
      NSUInteger release_count;
      NSUInteger retain_count = [self retainCount];
      release_count = [autorelease_class autoreleaseCountForObject:self];
      if (release_count > retain_count)
        [NSException
	  raise: NSGenericException
	  format: @"Autorelease would release object too many times.\n"
	  @"%"PRIuPTR" release(s) versus %"PRIuPTR" retain(s)",
	  release_count, retain_count];
    }

  (*autorelease_imp)(autorelease_class, autorelease_sel, self);
  return self;
}


+ (void) addObject: (id)anObj
{
  NSThread		*t = GSCurrentThread();
  NSAutoreleasePool	*pool;
  NSAssert(nil != t, @"Creating autorelease pool on nonexistent thread!");

  pool = t->_autorelease_vars.current_pool;
  if (pool == nil && t->_active == NO)
    {
        pool = t->_autorelease_vars.current_pool = [self new];
    }
  if (pool != nil)
    {
      (*pool->_addImp)(pool, @selector(addObject:), anObj);
    }
  else
    {
      NSAutoreleasePool	*arp = [NSAutoreleasePool new];

      if (anObj != nil)
	{
	  NSLog(@"autorelease called without pool for object (%p) "
	    @"of class %@ in thread %@", anObj,
	    NSStringFromClass([anObj class]), [NSThread currentThread]);
	}
      else
	{
	  NSLog(@"autorelease called without pool for nil object.");
	}
      [arp drain];
    }
}

autorelease用一种特殊的方法类实现。这种方法能够高效地运行应用程序中频繁调用的autorelease方法,它被成为“IMP Caching”(可用于iOS app性能优化)。在进行方法调用时,为了解决类名/方法名以及取得的方法运行时的函数指针,要在框架初始化时对其结果值进行缓存。

注:
NSAutoreleasePool * pool = [NSAutoreleasePool alloc] init];
[pool autorelease]; 发生crash,通常在使用Foundation框架时,无论调用哪一个对象的autorelease实例方法,实现上是调用的都是NSObject类的autorelease实例方法。但是对于NSAutoreleasePool类,autorelease实例方法已被该类冲载,因此运行时就会出错。