1

一種のフォローアップとして[[class alloc] init] から nil を返すことは良い習慣と見なされますか? 、あまり議論されていないケースがあります: 次の init を呼び出す前に、いくつかの前提条件に失敗した init をどうするか?

たとえば、この initWithStuff: メソッドで nil が渡されるか、一般に initWithValue: に渡す値がないことは絶対的な失敗であり、確実に nil を返したいとします。

- (id)initWithStuff:(Stuff *)inStuff {
  if (!inStuff || ![inStuff hasValidValue])
  {
    // can't proceed to call initWithValue: because we have no value
    // so do what?
    return nil;
  }
  NSInteger value = [inStuff integerValue];
  return [super initWithValue:value];
}

おそらくより明確な例は、ラップする指定されたイニシャライザ メソッドがオブジェクト ポインタを取り、nil が渡された場合に例外をスローする場合です。例外を引き起こす init 呼び出しを確実に短絡する必要があります。

私の推測: 可能な限り init してから、nil を返す前に self を解放します。必要に応じて、裸の init またはその他の初期化子を呼び出して、self を解放する前に既知の状態にします。

  // can't proceed to call super's initWithValue: because we have no value
  // so do what? do this:
  self = [super init]; // or initWithValue:0
  [self release];
  return nil;

そして、有効なデータなしで機能するそのような初期化子がなかった場合、有効なダミーデータを構築する必要があると思います。または、その作成者に文句を言って、それまでは nil を返して、リークに対処してください :^)

また、ARC は状況にどのように影響しますか?

私の推測では、可能な限り init を終了してから、nil を返すだけです。self を設定するのは冗長かもしれないと思うかもしれませんが、場合によってはそうではありません。いずれにせよ、コンパイラの警告を黙らせるためにそこにある必要があります。

  // can't proceed to call super's initWithValue: because we have no value
  // so do what? do this:
  self = [super init]; // finish init so ARC can release it having no strong references
  return nil;

私の推測は間違っていますか?

4

1 に答える 1

1

前提条件が満たされない場合は、 を呼び出さないのが理想的です[super init…]。(ARCを使用していない場合)リリースselfしてnilを返すだけです:

- (id)initWithStuff:(Stuff *)stuff {
    if (!stuff || ![stuff isValid]) {
        [self release]; // if not using ARC
        return nil;
    }

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

リリースはself、MRC の下で割り当て解除を処理します。ARC では、コンパイラがリリースを挿入します。

ただし、このアプローチには潜在的な問題があります。 ユーザーが解放selfすると (または ARC が解放すると)、システムはdeallocオブジェクトにメッセージを送信します。そして、あなたのdeallocメソッドは を呼び出します[super dealloc]。アンダーMRCを抑えることはでき[super dealloc]ますが、ARCでは回避できません。

したがって、スーパークラスがそのインスタンス変数の 1 つが初期化されていると想定し、その初期化された値に依存する危険性がありますdealloc。たとえば、これがスーパークラスであるとします。

@interface SomeSuperclass : NSObject
@end

@implementation SomeSuperclass {
    CFMutableBagRef bag;
}

- (id)init {
    if (self = [super init]) {
        bag = CFBagCreateMutable(NULL, 0, &kCFTypeBagCallBacks);
    }
    return self;
}

- (void)dealloc {
    CFRelease(bag);
}

@end

ここでの問題は、CFReleaseその引数が nil でないことを要求することです。[super init]したがって、サブクラスを呼び出さないと、割り当て解除中にクラッシュします。

この問題を考えると、最初の推奨事項を変更する必要があります。スーパークラスにこの種の問題がないことがわかっている場合dealloc(たとえば、ポインターを逆参照したり に渡す前にポインターをチェックしたりするためCFRelease)、安全に を呼び出すことはできません[super init]

自分のスーパークラスが安全かどうかわからない場合は、前提条件をクラス ファクトリ メソッドから出し入れすることをお勧めします。deallocinit

alloc/initつまり、クラスのパブリック インターフェイスの一部として扱わないでください。インスタンスを作成するためのクラス メソッドを提供します。

// The class factory method.  Declare this in your header file.  This is how you
// or any user of this class should create instances.
+ (id)myObjectWithStuff:(Stuff *)stuff {
    if (!stuff || ![stuff isValid])
        return nil;

    // self here is the class object, so it's appropriate to send `alloc` to it.
    // You don't want to hardcode the class name here because that would break
    // subclassing.
    return [[self alloc] initWithStuff:stuff];
}

// This is now considered a private method.  You should not declare it in your
// header file, though in Objective-C you can't prevent the user from calling it
// if he's determined to.
- (id)initWithStuff:(Stuff *)stuff {
    // Precondition was already checked in myObjectWithStuff:.
    if (self = [super init]) {
        // initialization here...
    }
    return self;
}
于 2012-11-16T02:40:14.717 に答える