web-dev-qa-db-ja.com

Objective-Cでは、self = [super init]がnilでないことを確認する必要があるのはなぜですか?

Objective-Cでのinitメソッドの作成に関する一般的な質問があります。

初期化を続行する前に、initメソッドがself = [super init]がnilでないことをチェックする必要があることはどこでも(Appleのコード、書籍、オープンソースコードなど)確認できます。

InitメソッドのデフォルトのAppleテンプレートは次のとおりです。

- (id) init
{
    self = [super init];

    if (self != nil)
    {
        // your code here
    }

    return self;
}

どうして?

Initがいつnilを返すのか。 NSObjectでinitを呼び出してnilが返された場合、何かが本当にねじ込まれているはずですよね?そして、その場合、あなたはプログラムを書くことさえしないかもしれません...

クラスのinitメソッドがnilを返すことは本当に一般的ですか?もしそうなら、どのような場合、そしてなぜですか?

162
Jasarien

例えば:

[[NSData alloc] initWithContentsOfFile:@"this/path/doesn't/exist/"];
[[NSImage alloc] initWithContentsOfFile:@"unsupportedFormat.sjt"];
[NSImage imageNamed:@"AnImageThatIsntInTheImageCache"];

... 等々。 (注:ファイルが存在しない場合、NSDataは例外をスローする場合があります)。問題が発生したときにnilを返すことが予想される動作である領域がかなりあります。そのため、一貫性のために、ほとんど常にnilをチェックすることが標準的な方法です。

52
iKenndac

この特定のイディオムは、すべての場合に機能するため標準です。

まれですが、次の場合があります...

[super init];

...は異なるインスタンスを返すため、selfへの割り当てが必要です。

また、nilを返す場合があります。そのため、コードがもう存在しないインスタンス変数スロットを初期化しようとしないように、nilチェックが必要です。

一番下の行は、それが使用するために文書化された正しいパターンであり、それを使用していない場合、あなたはそれを間違っているということです。

50
bbum

ほとんどのクラスで、[super init]からの戻り値がnilであり、標準的な慣行で推奨されているようにチェックし、nilの場合は時期尚早に戻ると、基本的にアプリはまだ正しく動作しません。考えてみると、(self!= nil)チェックがある場合でも、クラスを適切に動作させるには、実際に99.99%の時間do selfが非nilである必要があります。ここで、何らかの理由で[super init] did return nilと仮定します。基本的に、nilに対するチェックは基本的にクラスの呼び出し元に渡されます。呼び出しが成功したと自然に仮定します。

基本的に、私が得ているのは、99.99%の時間です。if(self!= nil)は、堅牢性の観点からは何も買わないということです。これを本当に堅牢に処理するには、呼び出し階層全体にチェックを入れる必要があります。そして、それでも、あなたが購入する唯一のことは、アプリがもう少しきれい/堅牢に失敗することです。しかし、それでも失敗します。

ライブラリクラスが[super init]の結果としてnilを返すことをdecided意的に決定した場合、とにかくかなりうんざりしていることになります。これは、ライブラリクラスの作成者が実装を間違えたことを示しています。

アプリがはるかに限られたメモリで実行された場合、これはレガシーコーディングの提案に近いと思います。

しかし、Cレベルのコードの場合、通常はNULLポインターに対してmalloc()の戻り値をチェックします。一方、Objective-Cの場合、反対の証拠が見つかるまで、一般的にif(self!= nil)チェックをスキップすると思います。なぜ矛盾するのですか?

なぜなら、Cおよびmallocレベルでは、場合によっては実際に部分的に回復できるからです。 Objective-Cでは、99.99%のケースで、[super init]がnilを返す場合、それを処理しようとしても、基本的にf *** edです。アプリをクラッシュさせて余波に対処することもできます。

25
Gino

これは上のコメントの要約のようなものです。

スーパークラスがnilを返すとしましょう。何が起こるの?

規約に従わない場合

initメソッドの途中でコードがクラッシュします。 (initが意味をなさない限り)

慣習に従うと、スーパークラスがnilを返すことを知らない(ほとんどの人はここに来る)

あなたのインスタンスはnilであるため、あなたのコードはある時点でクラッシュするでしょう。または、プログラムはクラッシュせずに予期しない動作をします。まあ!これが欲しい?知りません...

あなたが慣例に従っているなら、あなたのサブクラスが喜んでnilを返すことを許可する

コードのドキュメント(!)に「returns ... or nil」と明記する必要があります。これを処理するために、残りのコードを準備する必要があります。今では理にかなっています。

8

通常、クラスがNSObjectから直接派生している場合、その必要はありません。ただし、クラスが他のクラスから派生している場合、それらのイニシャライザがnilを返す可能性があるため、取得するのが良い習慣です。

はい、記録のために、ベストプラクティスに従って、NSObjectから直接派生するクラスも含め、すべてのクラスでそれを記述します。

7
John Rudy

よくある間違いは、書くことです

self = [[super alloc] init];

これはスーパークラスのインスタンスを返しますが、これはサブクラスのコンストラクター/ initに必要なものではありません。サブクラスメソッドに応答しないオブジェクトを取得すると、混乱する可能性があり、見つからないメソッドや識別子に応答しないなどの紛らわしいエラーを生成します。

self = [super init]; 

スーパークラスにメンバー(変数または他のオブジェクト)があり、サブクラスのメンバーを設定する前にfirstを初期化する場合に必要です。それ以外の場合、objcランタイムはすべてをまたはnilに初期化します。 (ANSI Cとは異なり、メモリチャンクをまったくクリアせずに割り当てることが多い

はい、基本クラスの初期化は、メモリ不足エラー、コンポーネントの欠落、リソース取得の失敗などのために失敗する可能性があるため、nilのチェックは賢明で、数ミリ秒未満で完了します。

3
Chris Reid

あなたは正しいです、あなたはしばしば_[super init]_と書くことができますが、それは何のサブクラスに対しても機能しません。人々はたった1つの標準コード行を記憶し、それが時々必要なときでも常に使用することを好むため、nilが返される可能性と可能性の両方をとる標準if (self = [super init])を取得します。アカウントに返されるself以外のオブジェクトの.

3
andyvn22

これは、初期化が機能したことを確認することです。initメソッドがnilを返さなかった場合、ifステートメントはtrueを返すため、オブジェクトの作成を確認する方法は正しく機能しました。 initが失敗する可能性があると考える理由はほとんどありませんが、スーパークラスが知らないオーバーライドされたinitメソッドなどがありますが、それが一般的だとは思わないでしょう。しかし、それが発生した場合、クラッシュする可能性はないので、常にチェックしています...

2
Daniel

OS Xでは、-[NSObject init]は、メモリ上の理由により失敗します。 iOSについても同じことが言えません。

また、何らかの理由でnilを返す可能性のあるクラスをサブクラス化する場合は、作成することをお勧めします。

1
MaddTheSane