2009-12-01 5 views
11

複数のクラスのオブジェクトを受け入れるか返すメソッドを独自のコードで実装するときは、常に最も特定のスーパークラスを使用しようとします。たとえば、入力に応じてNSArray *またはNSDictionary *を返すメソッドを実装しようとした場合、そのメソッドはNSObject *の戻り型になります。なぜなら、それは最も直接的な共通スーパークラスなのでです。ここでは例です:(NSObject *)がより正確になる場合、なぜメソッドシグネチャで(id)を使用するのですか?

@interface MyParser() 
- (BOOL)stringExpressesKeyValuePairs:(NSString *)string; 
- (BOOL)stringExpressesAListOfEntities:(NSString *)string; 
- (NSArray *)parseArrayFromString:(NSString *)string; 
- (NSDictionary *)parseDictionaryFromString:(NSString *)string; 
@end 

@implementation MyParser 
- (NSObject *)parseString:(NSString *)string { 
    if ([self stringExpressesKeyValuePairs:string]) { 
     return [self parseDictionaryFromString:string]; 
    } 
    else if ([self stringExpressesAListOfEntities:string]) { 
     return [self parseArrayFromString:string]; 
    } 
} 
// etc... 
@end 

私は(NSObjectの*)は、より正確になり、アップルの用途(ID)は、特定のメソッドのシグネチャで財団と他のAPIの多くの例を気づきました。例えば、ここでNSPropertyListSerializationの方法です:

+ (id)propertyListFromData:(NSData *)data 
      mutabilityOption:(NSPropertyListMutabilityOptions)opt 
        format:(NSPropertyListFormat *)format 
      errorDescription:(NSString **)errorString 

この方法から可能な戻り値の型はNSDataの、NSStringの、NSArrayの、NSDictionaryの、NSDate、およびのNSNumberです。呼び出し元は、型キャストなしでretainのようなNSObjectメソッドを呼び出せるため、(NSObject *)の戻り型が(id)よりも良い選択となるようです。

私は一般的に正式なフレームワークによって確立されたイディオムをエミュレートしようとしますが、私はそれらの動機付けを理解したいと思っています。私はAppleがこのような場合に(ID)を使用するいくつかの正当な理由があると確信していますが、私はそれを見ていないだけです。私は何が欠けていますか?

+0

NSProxyはNSObjectではありません。 NSObjectは唯一のルートクラスではないので、すべてがそのサブクラスになるという安全な前提はありません。 –

答えて

8

idを使用すると、コンパイラは不明な型のオブジェクトになります。 NSObjectを使用すると、コンパイラはNSObjectで使用できるメッセージのみを使用することを期待します。だから...配列が返され、それがidとしてキャストされていることがわかっている場合は、コンパイラの警告なしでobjectAtIndex:を呼び出すことができます。 NSObjectのキャストを返すのに対して、警告が表示されます。

1

キャストせずにすでにタイプidのポインタに対して-retainを呼び出すことができます。特定のスーパークラス型を使用する場合、コンパイラの警告を避けるために、サブクラスのメソッドを呼び出すたびにポインタをキャストする必要があります。コンパイラはあなたに警告せず、あなたの意図をよりよく表すためにidを使います。

19

(ID)がメソッド宣言に使用する理由は2つある:

(1)の方法は、任意の型を取るか、または返すことができます。 NSArrayには任意のランダムオブジェクトが含まれているため、objectAtIndex:はランダムタイプのオブジェクトを返します。 NSObject*またはid <NSObject>にキャストすることには2つの理由があります。特定の小さなメソッドセットを実装している限り、配列にはNSObject以外のサブクラスを含めることができます。第2に、特定の戻り値の型はキャストが必要です。

(2)Objective-Cは共変の宣言をサポートしていません。考えてみましょう:

@interface NSArray:NSObject 
+ (id) array; 
@end 

さて、あなたはNSArrayNSMutableArray両方に+arrayを呼び出すことができます。前者は不変の配列を返し、後者は可変の配列を返します。 Objective-Cの共存宣言のサポートがないため、上記が返されたとして(NSArray*)と宣言された場合、サブクラスメソッドのクライアントは `(NSMutableArray *)にキャストする必要があります。醜い、壊れやすく、エラーが起こりやすい。したがって、ジェネリック型の使用は、一般的に最も簡単な解決策です。

特定のクラスのインスタンスを返すメソッドを宣言する場合は、明示的に型キャストします。オーバーライドされるメソッドを宣言していて、オーバーライドがサブクラスを返す場合、サブクラスを返すという事実はクライアントに公開され、次に(id)を使用します。

バグを報告する必要はありません。すでにいくつかあります。


にObjCは今instancetypeキーワードによって制限された共同分散をサポートしていることに注意してください。

I.e. NSArray年代+アレイ法は今のように宣言することができます

+ (instancetype) array; 

コンパイラは[NSArray array]NSArray*を返すとみなされるだろうがNSMutableArray*を返すよう[NSMutableArray array]を扱っていました。

1

(id)は、オブジェクトをより簡単にサブクラス化できるようにするためによく返されます。たとえば、イニシャライザやコンビニエンスメソッドでは、return(id)は、特別な理由がない限り、サブクラスがスーパークラスのメソッドをオーバーライドする必要がないことを意味します。

関連する問題