Objective-C:idとvoidの違い*


回答:


240

void * 「型なし/不明な内容のランダムなチャンクのメモリへの参照」を意味します

id 「不明なクラスのランダムなObjective-Cオブジェクトへの参照」を意味します

追加の意味上の違いがあります:

  • GC OnlyまたはGC Supportedモードでは、コンパイラーはtypeの参照に対しては書き込みバリアを発行しますが、typeに対しては発行しidませんvoid *。構造体を宣言する場合、これは重大な違いになる可能性があります。のようvoid *_superPrivateDoNotTouch;にiVarを宣言する_superPrivateDoNotTouchと、実際にオブジェクトである場合に、オブジェクトの早すぎる刈り取りが発生します。しないでください。

  • void *タイプの参照でメソッドを呼び出そうとすると、コンパイラ警告が表示されます。

  • id型に対してメソッドを呼び出そうとすると、呼び出されているメソッドが@interfaceコンパイラから見たどの宣言でも宣言されていない場合にのみ警告が表示されます。

したがって、オブジェクトをとして参照することはできませんvoid *。同様に、id型付き変数を使用してオブジェクトを参照することは避けてください。できる限り具体的なクラス型付き参照を使用します。NSObject *より良好でありid、コンパイラは、少なくとも、その基準に対するメソッド呼び出しのより良好な検証を提供することができるからです。

の一般的で有効な用途の1つvoid *は、他のAPIを介して渡される不透明なデータ参照としてです。

次のsortedArrayUsingFunction: context:方法を検討してくださいNSArray

- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context;

ソート関数は次のように宣言されます。

NSInteger mySortFunc(id left, id right, void *context) { ...; }

この場合、NSArrayは、context引数として渡されたものを引数としてメソッドに渡すだけですcontext。NSArrayに関する限り、これはポインターサイズのデータ​​の不透明な塊であり、必要に応じて自由に使用できます。

言語にクロージャー型の機能がない場合、これは関数でデータの塊を運ぶ唯一の方法です。例; mySortFunc()で条件付きで大文字と小文字を区別する、または区別しないで並べ替えたいが、スレッドセーフでもある場合は、コンテキストでis-case-sensitiveインジケーターを渡します。

壊れやすく、エラーが発生しやすいが、唯一の方法。

ブロックはこれを解決します-ブロックはCのクロージャーです。ブロックはClang- http://llvm.org/で利用可能で、Snow Leopard(http://developer.apple.com/library/ios/documentation/Performanceで広く使用されています) /Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf)。


さらに、a はand idに応答するもの-retainと想定されていますが-release、a void*は呼び出し先に対して完全に不透明です。任意のポインターを-performSelector:withObject:afterDelay:(オブジェクトを保持する)に渡すことはできません。また、それが+[UIView beginAnimations:context:]コンテキストを保持することは想定できません(アニメーションデリゲートはコンテキストの所有権を維持する必要があります。UIKitはアニメーションデリゲートを保持します)。
tc。

3
が何にid応答するかについての想定はできません。id簡単から本来のないクラスのインスタンスを参照することができますNSObject。ただし、実際には、あなたの発言は実際の行動と最もよく一致します。非<NSObject>実装クラスをFoundation APIと混在させて遠くに行くことはできません。
bbum

2
現在idClass型はARCで保持可能なオブジェクトポインターとして扱われています。したがって、この仮定は少なくともARCでは真実です。
eonil

「早刈り」とは?
ブラッド・トーマス

1
@BradThomasプログラムが完了する前にガベージコレクターがメモリを収集するとき。
bbum 2017年

21

idは、目的のCオブジェクトへのポインターです。ここで、void *は、何かへのポインターです。

idは、不明なmthodsの呼び出しに関連する警告もオフにします。たとえば、次のようにします。

[(id)obj doSomethingWeirdYouveNeverHeardOf];

未知のメソッドに関する通常の警告は表示されません。もちろん、objがnilでないか、本当にそのメソッドを実装していない限り、実行時に例外が発生します。

多くの場合、を使用するNSObject*id<NSObject>、を優先してid、少なくとも返されたオブジェクトがCocoaオブジェクトであることを確認する必要があるため、retain / release / autoreleaseなどのメソッドを安全に使用できます。


2
メソッド呼び出しの場合、ターゲットメソッドがどこにも宣言されていない場合、タイプ(id)のターゲットは警告を生成します。したがって、あなたの例では、警告が出ないようにdoSomethingWeirdYouveNeverHeardOfをどこかで宣言する必要があったでしょう。
bbum 2009

そうですね、より良い例はstoragePolicyのようなものです。
ピーターNルイス

@PeterNLewisのOften you should use NSObject*代わりに同意しませんid。指定するNSObject*ことで、実際にはオブジェクトがNSObjectであると明示的に言っています。オブジェクトへのメソッド呼び出しは警告になりますが、そのオブジェクトが実際にメソッド呼び出しに応答する限り、実行時例外は発生しません。警告は明らかに迷惑なのでid、より良いです。次に、たとえばと言ってid<MKAnnotation>、より具体的にすることができます。この場合、オブジェクトは何でも、MKAnnotationプロトコルに準拠している必要があります。
pnizzle

1
id <MKAnnotation>を使用する場合は、NSObject <MKAnnotation> *を使用することもできます。これにより、MKAnnotationの任意のメソッドとNSObjectの任意のメソッド(つまり、通常のNSObjectルートクラス階層のすべてのオブジェクト)を使用できるようになり、他のすべてに対して警告が表示されます。ランタイムクラッシュ。
Peter N Lewis

8

メソッドに戻りの型がある場合、id任意のObjective-Cオブジェクトを返すことができます。

void つまり、メソッドは何も返しません。

void *単なるポインタです。ポインタが指すアドレスの内容を編集することはできません。


2
メソッドの戻り値に当てはまるので、ほぼ正しいです。変数または引数の宣言に適用されるため、そうではありません。また、コンテンツを読み書きしたい場合は、いつでも(void *)をより具体的な型にキャストできます。そうすることはお勧めできません。
bbum 2009

8

idObjective-Cオブジェクトへのポインタです。void *ポインタであるもの。のvoid *代わりにを使用することもできますがid、コンパイラの警告が表示されないため、これはお勧めできません。

あなたは見てみたいことがありstackoverflow.com/questions/466777/whats-the-difference-between-declaring-a-variable-id-and-nsobjectunixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs -id.html


1
結構です。(void *)型付き変数をメソッド呼び出しのターゲットにすることはできません。その結果、コンパイラから「警告:無効なレシーバータイプ 'void *'」が表示されます。
bbum 2009

@bbum:void *型付き変数は、間違いなくメソッド呼び出しのターゲットになる可能性があります。これはエラーではなく警告です。:だけでなく、あなたがこれを行うことができ、それint i = (int)@"Hello, string!";とフォローアップ:printf("Sending to an int: '%s'\n", [i UTF8String]);。これは警告であり、エラーではありません(正確には推奨されておらず、移植性もありません)。しかし、あなたはこれらの事を行うことができる理由は、すべての基本的なC.ある
ヨーネ

1
ごめんなさい。あなたは正しいです; エラーではなく警告です。私は警告を常にどこでもエラーとして扱います。
bbum 2009

4
/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

上記のコードはobjc.hからのものであるため、idはobjc_object構造体のインスタンスであり、isaポインターは任意のObjective C Classオブジェクトにバインドできますが、void *は単なる型指定されていないポインターです。


2

私の理解では、idはオブジェクトへのポインタを表しますが、使用したい型にキャストする限り、void *は実際には何かを指すことができます。


(void *)からidを含むいくつかのオブジェクトタイプにキャストしている場合は、それが間違っている可能性が非常に高いです。そうする理由はありますが、それらはほとんどなく、ほとんど常に設計上の欠陥を示しています。
bbum 2009

1
「そうする理由はありますが、その数はわずかです」と引用します。状況によります。しかし、私はいくつかのコンテキストなしで「あなたはそれを間違っている可能性が非常に高い」のような包括的な陳述をしません。
hhafez 2009

私は包括的な声明を出します。間にvoid *を指定して間違った型にキャストしたため、あまりにも多くの忌まわしいバグを探し出して修正する必要がありました。1つの例外は、void *コンテキスト引数を取るコールバックベースのAPIで、その契約では、コールバックのセットアップとコールバックの受信の間、コンテキストは変更されないままであると規定されています。
bbum 2009

0

既に述べたことに加えて、コレクションに関連するオブジェクトとポインタには違いがあります。たとえば、NSArrayに何かを入れたい場合、(「id」型の)オブジェクトが必要であり、そこに(「void *」型の)生データポインタを使用することはできません。コレクション内で使用[NSValue valueWithPointer:rawData]するためvoid *rawDdataに、「id」タイプに変換するために使用できます。一般に、「id」はより柔軟であり、それに接続されているオブジェクトに関連するセマンティクスが多くなります。Objective Cのidタイプを説明する他の例がここにあります

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.