107

As a Java developer who is reading Apple's Objective-C 2.0 documentation: I wonder what "sending a message to nil" means - let alone how it is actually useful. Taking an excerpt from the documentation:

There are several patterns in Cocoa that take advantage of this fact. The value returned from a message to nil may also be valid:

  • If the method returns an object, any pointer type, any integer scalar of size less than or equal to sizeof(void*), a float, a double, a long double, or a long long, then a message sent to nil returns 0.
  • If the method returns a struct, as defined by the Mac OS X ABI Function Call Guide to be returned in registers, then a message sent to nil returns 0.0 for every field in the data structure. Other struct data types will not be filled with zeros.
  • If the method returns anything other than the aforementioned value types the return value of a message sent to nil is undefined.

Has Java rendered my brain incapable of grokking the explanation above? Or is there something that I am missing that would make this as clear as glass?

I do get the idea of messages/receivers in Objective-C, I am simply confused about a receiver that happens to be nil.

4

11 に答える 11

92

ええと、非常に不自然な例を使用して説明できると思います。ArrayList 内のすべての要素を出力する Java のメソッドがあるとします。

void foo(ArrayList list)
{
    for(int i = 0; i < list.size(); ++i){
        System.out.println(list.get(i).toString());
    }
}

さて、そのメソッドを次のように呼び出すと: someObject.foo(NULL); リストにアクセスしようとすると、おそらく NullPointerException が発生するでしょう。この場合は、list.size(); の呼び出しです。さて、そのような NULL 値で someObject.foo(NULL) を呼び出すことはおそらくないでしょう。ただし、someObject.foo(otherObject.getArrayList()); のような ArrayList を生成するエラーが発生した場合、NULL を返すメソッドから ArrayList を取得した可能性があります。

もちろん、次のような場合にも問題が発生します。

ArrayList list = NULL;
list.size();

現在、Objective-C には同等のメソッドがあります。

- (void)foo:(NSArray*)anArray
{
    int i;
    for(i = 0; i < [anArray count]; ++i){
        NSLog(@"%@", [[anArray objectAtIndex:i] stringValue];
    }
}

さて、次のコードがあるとします。

[someObject foo:nil];

Java が NullPointerException を生成する同じ状況があります。nil オブジェクトは [anArray count] で最初にアクセスされますが、NullPointerException をスローする代わりに、Objective-C は上記の規則に従って単純に 0 を返すため、ループは実行されません。ただし、ループを一定回数実行するように設定すると、最初に [anArray objectAtIndex:i] の anArray にメッセージが送信されます。これも 0 を返しますが、objectAtIndex: はポインターを返し、0 へのポインターは nil/NULL であるため、ループのたびに NSLog に nil が渡されます。(NSLog はメソッドではなく関数ですが、nil NSString が渡されると (null) が出力されます。

場合によっては、NullPointerException を使用した方がよい場合があります。これは、プログラムに問題があることをすぐに判断できるためですが、例外をキャッチしないと、プログラムがクラッシュします。(C では、このように NULL を逆参照しようとすると、プログラムがクラッシュします。) Objective-C では、実行時の動作が正しくない可能性があります。ただし、0/nil/NULL/ゼロ化された構造体を返しても壊れないメソッドがある場合、これにより、オブジェクトまたはパラメーターが nil であることを確認するためにチェックする必要がなくなります。

于 2008-10-01T06:32:29.183 に答える
51

へのメッセージは何もせず、、、、またはnilを返します。nilNilNULL00.0

于 2008-11-10T02:44:22.340 に答える
41

他の投稿はすべて正しいですが、ここで重要なのはコンセプトかもしれません。

Objective-C のメソッド呼び出しでは、セレクターを受け入れることができるオブジェクト参照はすべて、そのセレクターの有効なターゲットです。

これにより、「ターゲット オブジェクトはタイプ X ですか?」という手間が大幅に省けます。コード - 受信オブジェクトがセレクターを実装している限り、それがどのクラスであってもまったく違いはありません! nil任意のセレクターを受け入れる NSObject です -何もしません。これにより、多くの「nil をチェックし、true の場合はメッセージを送信しない」というコードも不要になります。(「それを受け入れれば、それを実装する」という概念は、プロトコルを作成できるようにするものでもあります。これは、Java インターフェイスのようなものです。クラスが指定されたメソッドを実装する場合、プロトコルに準拠するという宣言です。)

この理由は、コンパイラを満足させる以外に何もしないモンキー コードを排除するためです。はい、もう 1 つのメソッド呼び出しのオーバーヘッドが発生しますが、CPU 時間よりもはるかに高価なリソースであるプログラマーの時間を節約できます。さらに、アプリケーションからより多くのコードと条件付きの複雑さを排除しています。

反対票を投じた人のために明確にする: これは良い方法ではないと思うかもしれませんが、それは言語の実装方法であり、Objective-C で推奨されるプログラミング イディオムです(Stanford iPhone プログラミング レクチャーを参照してください)。

于 2008-11-21T21:15:08.703 に答える
18

つまり、nil ポインターで objc_msgSend が呼び出されたときにランタイムがエラーを生成しないということです。代わりに、何らかの (しばしば役立つ) 値を返します。副作用がある可能性のあるメッセージは何もしません。

ほとんどのデフォルト値はエラーよりも適切であるため、便利です。例えば:

[someNullNSArrayReference count] => 0

つまり、nil は空の配列のように見えます。nil NSView 参照を非表示にしても何も起こりません。便利ですね。

于 2008-10-01T06:12:11.353 に答える
12

Greg Parkerサイトから:

LLVM Compiler 3.0 (Xcode 4.2) 以降を実行している場合

戻り値の型 | を持つ nil へのメッセージ 戻る
64 ビットまでの整数 | 0
long double までの浮動小数点 | 0.0
ポインター | なし
構造体 | {0}
任意の _Complex 型 | {0, 0}
于 2012-02-29T22:42:18.087 に答える
12

ドキュメントからの引用には、2 つの別個の概念があります。

Cocoa には、この事実を利用するパターンがいくつかあります。

メッセージから nil に返される値も有効な場合があります。

ここではおそらく前者の方が適切です。通常、メッセージを送信できるとnil、コードがより簡単になります。どこでも null 値をチェックする必要はありません。標準的な例は、おそらくアクセサ メソッドです。

- (void)setValue:(MyClass *)newValue {
    if (value != newValue) { 
        [value release];
        value = [newValue retain];
    }
}

へのメッセージ送信が有効でない場合、この方法はより複雑になります。メッセージを送信する前にnil確認するためvalueに、さらに 2 つのチェックを行う必要newValueがあります。nil

ただし、後者の点 (メッセージから返される値nilも通常は有効である) は、前者に乗数効果を追加します。例えば:

if ([myArray count] > 0) {
    // do something...
}

このコードも値のチェックを必要とせず、nil自然に流れます...

とはいえ、メッセージを送信できる追加の柔軟性にnilは、いくらかのコストがかかります. ある段階で、値がnil.

于 2008-10-12T20:02:55.137 に答える
9

これは、多くの場合、安全のためにどこでも nil オブジェクトをチェックする必要がないことを意味します - 特に:

[someVariable release];

または、前述のように、nil 値を取得した場合、さまざまな count メソッドと length メソッドはすべて 0 を返すため、nil のチェックをすべて追加する必要はありません。

if ( [myString length] > 0 )

またはこれ:

return [myArray count]; // say for number of rows in a table
于 2008-10-01T06:53:01.460 に答える
6

「レシーバーが nil である」と考えないでください。私は同意します、それかなり奇妙です。メッセージを nil に送信している場合、受信者はありません。あなたはただメッセージを何も送信していません。

これにどう対処するかは、Java と Objective-C の哲学的な違いです。Java では、それはエラーです。Objective-C ではノーオペレーションです。

于 2008-10-02T00:56:57.483 に答える
6

nil に送信され、戻り値のサイズが sizeof(void*) よりも大きい ObjC メッセージは、PowerPC プロセッサで未定義の値を生成します。それに加えて、これらのメッセージにより、Intel プロセッサでもサイズが 8 バイトを超える構造体のフィールドに未定義の値が返されます。Vincent Gable は、彼のブログ投稿でこれをうまく説明しています

于 2009-03-14T21:41:19.617 に答える
6

他の回答でこれが明確に言及されているとは思いません。Java に慣れている場合は、Mac OS X の Objective-C は例外処理をサポートしていますが、これはオプションの言語機能であることに注意してください。コンパイラフラグでオン/オフします。私の推測では、「にメッセージを送信することnilは安全です」というこの設計は、言語に例外処理サポートが含まれる前に行われ、同様の目標を念頭に置いて行われました。メソッドnilはエラーを示すために戻ることができ、メッセージを送信するとnil通常はnilこれにより、エラー表示がコード全体に伝播されるため、メッセージごとにチェックする必要がなくなります。重要なポイントでのみ確認する必要があります。個人的には、例外の伝播と処理はこの目標に対処するためのより良い方法だと思いますが、誰もがそれに同意するわけではありません. (一方、たとえば、メソッドがスローする可能性のある例外を宣言しなければならないという Java の要件は好きではありません。これにより、多くの場合、コード全体に例外宣言を構文的に伝播することが強制されます。ただし、それは別の議論です。)

関連する質問「目的の C では、すべてのオブジェクトの作成が成功したことを主張する必要がありますか?」に対して、同様の、しかしより長い回答を投稿しました。詳細が必要な場合。

于 2011-04-03T15:41:10.557 に答える