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

发布于 2024-11-06 18:52:44 字数 848 浏览 3 评论 0原文

我正在学习 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 相关,然后再次与成员相关,并且命名该对象只是为了以后能够释放它。它还违反了指定的初始值设定项习惯用法。

有没有更惯用的方法来做到这一点,而不使用自动释放池?

I'm learning Objective-C. In my first non-trivial project I've run into a question about how to best handle resources passed to the initializer, compared to the default initializer. My class has a retained resource, engine, which can be set manually after creation, or during initialization explicitly, or during initialization by default:

- (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;
}

The default initializer, in particular, seems very ugly to me, interleaving code relevant to the self, then the member, then self again, then the member again, and naming the object solely to be able to release it later. It also violates the designated initializer idiom.

Is there a more idiomatic way to do this, without using autorelease pools?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

初熏 2024-11-13 18:52:44

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

人们通常不会谈论“默认初始化程序”,而是谈论“指定初始化程序”。指定的初始化程序是所有其他初始化程序都调用的初始化程序——它通常是允许最多自定义的初始化程序。在本例中,-initWithEngine: 是您指定的初始值设定项。

Are you talking about e in -init? I don't see the problem there... you create an object, you use it, you release it. That's the usual and correct pattern for creating and using objects. If you're unhappy with that, though, you could instead have -init pass nil into -initWithEngine: and have -initWithEngine: create the engine if none is provided.

People don't usually talk about a "default initializer" so much as a "designated initializer." The designated initializer is the one that all the other initializers call -- it's often one that allows for the most customization. In this case, -initWithEngine: is your designated initializer.

葵雨 2024-11-13 18:52:44
- 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;
}

这也是一个很好的例子,说明了为什么在一个类上避免使用多个初始化程序是明智的。跟踪可能会很痛苦,并且会使子类化变得更加乏味且容易出错。最好有一个初始值设定项和多个子类,或者有一个初始值设定项并允许在事后配置实例。

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


假设您正在声明一个 Car 类,我会这样做:

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

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

这为您提供了一个初始值设定项,使子类化变得非常简单明显。

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

If you have "cover initializers", they should follow the same pattern:

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

This is also a good example of why it is wise to avoid multiple initializers on a class. It can be a pain to keep track and makes subclassing more tedious and error prone. Better to have one initializer and multiple subclasses or have a single initializer and allow the instances to be configured after the fact.

I.e. instead of initWithBob:, have @property(retain) Bob* bob; that can be called after init as needed. Limiting initializers to required state is a good rule of thumb.


Assuming you are declaring a Car class, I'd do something like:

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

The implementations should be obvious; car creates a default engine and calls alloc/initWithEngine:/autorelease.

That gives you a single initializer, making sub-classing dead simple obvious.

送君千里 2024-11-13 18:52:44
- (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 方法中调用属性 setter/getter(我假设您已经声明了保留属性并且只是覆盖了 setter)。

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

- (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;
}

You've done basically the right thing. The only thing I would change is directly assigning the variable and retaining it rather than calling the setter in the init method.

Apple recommends not calling property setters/getters in the init and dealloc methods (I'm assuming you've declared a retain property and are just overriding the setter).

If you have engine customization code that needs to happen the first time the engine is set in the initializer as well as whenever it is changed, then you should refactor this out into a separate method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文