在Obj-C初始化期间管理引用计数的惯用方法是什么?

时间:2011-05-11 20:13:24

标签: objective-c memory-management idioms

我正在学习Objective-C。在我的第一个非平凡的项目中,我遇到了一个关于如何最好地处理传递给初始化程序的资源的问题,与默认的初始化程序相比。我的类有一个保留资源engine,可以在创建后手动设置,也可以在显式初始化期间或默认情况下在初始化期间设置:

- (id)init {
    if ((self = [super init])) {
        id e = [[XorShiftEngine alloc] init];
        [self setEngine: e];
        [e release];
    }
    return self;
}

- (id)initWithEngine:(NSObject <RandEngine> *)e {
    if ((self = [super init]))
        [self setEngine: e];
    return self;
}

- (id)setEngine:(NSObject <RandEngine> *)newEngine {
    [newEngine retain];
    [engine release];
    engine = newEngine;
    // Some other stuff which needs to happen on changing the engine.
    return engine;
}

默认情况下,默认的初始化程序对我来说似乎非常难看,交错代码与self相关,然后是成员,然后是self,然后是成员,然后再命名对象,以便稍后能够释放它。它也违反了指定的初始化程序习惯用法。

在不使用自动释放池的情况下,是否有更惯用的方法?

3 个答案:

答案 0 :(得分:3)

- init
{
    self = [super init];
    if (self != nil) {
        // initialization here
    }
    return self;
}

如果你有“封面初始化者”,他们应该遵循相同的模式:

- initWithBob:(Bob*)aBob
{
   self = [self init];
   if ( self != nil ) {
       ... deal with aBob here ...
   }
   return self;
}

这也是为什么避免在类上使用多个初始化器是明智的一个很好的例子。跟踪并使子类化更加繁琐且容易出错,这可能是一种痛苦。最好有一个初始化程序和多个子类,或者只有一个初始化程序,并允许在事实之后配置实例。

即。而不是initWithBob:,可以根据需要在@property(retain) Bob* bob;之后调用init。将初始化程序限制为所需状态是一个很好的经验法则。


假设您正在宣布一个Car类,我会做类似的事情:

@interface Car:NSObject
+ car;
+ carWithEngine:(Engine*)anEngine;
- initWithEngine:(Engine*)anEngine;
@end

实施应该是显而易见的; car创建默认引擎并调用alloc/initWithEngine:/autorelease

这为你提供了一个初始化程序,使得子类分类变得简单明了。

答案 1 :(得分:3)

您是在谈论e中的-init吗?我没有看到那里的问题...你创建一个对象,你使用它,你释放它。这是创建和使用对象的通常和正确的模式。但是,如果您对此不满意,则可以将-init传递给-initWithEngine:,如果没有提供,则-initWithEngine:创建引擎。

人们通常不会像“指定的初始化程序”那样谈论“默认初始化程序”。指定的初始化程序是所有其他初始化程序调用的程序 - 它通常是允许最多定制的程序。在这种情况下,-initWithEngine:是您指定的初始化程序。

答案 2 :(得分:2)

- (id)init {
    id e = [[XorShiftEngine alloc] init];
    self = [self initWithEngine: e];
    [e release];
    return self;
}

// designated initializer
- (id)initWithEngine:(NSObject <RandEngine> *)e {
    self = [super init];
    if (self != nil) {
        engine = [e retain];
        // engine initialization stuff
    }
    return self;
}

- (id)setEngine:(NSObject <RandEngine> *)newEngine {
    [newEngine retain];
    [engine release];
    engine = newEngine;
    // Some other stuff which needs to happen on changing the engine.
    return engine;
}

你做的基本上是正确的。我唯一要改变的是直接分配变量并保留它而不是在init方法中调用setter。

Apple建议不要在init和dealloc方法中调用属性设置器/ getter(我假设你已经声明了一个retain属性并且只是覆盖了setter)。

如果您的引擎自定义代码需要在初始化程序中第一次设置引擎时以及更改时发生,那么您应该将其重构为单独的方法。