11

複数のクラスのオブジェクトを受け入れたり返したりできるメソッドを独自のコードに実装するときはいつでも、利用可能な最も具体的なスーパークラスを使用しようとします。たとえば、入力に応じて NSArray * または NSDictionary * を返す可能性のあるメソッドを実装する場合、そのメソッドに NSObject * の戻り型を指定します。これは最も直接的な共通のスーパークラスだからです。次に例を示します。

@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

Foundation やその他の API で、(NSObject *) の方が正確な場合に、Apple が特定のメソッド シグネチャで (id) を使用する多くのケースに気付きました。たとえば、NSPropertyListSerialization のメソッドは次のとおりです。

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

このメソッドから返される可能性のある型は、NSData、NSString、NSArray、NSDictionary、NSDate、および NSNumber です。(NSObject *) の戻り値の型は、(id) よりも適切な選択であると思われます。これは、呼び出し元が型キャストなしで保持のような NSObject メソッドを呼び出すことができるためです。

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

4

4 に答える 4

19

メソッド宣言で (id) が使用される理由は 2 つあります。

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

(2) Objective-C は共変宣言をサポートしていません。検討:

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

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

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

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


instancetypeObjC では、キーワードによる共分散のサポートが制限されていることに注意してください。

つまりNSArray、 +array メソッドは次のように宣言できるようになりました。

+ (instancetype) array;

また、コンパイラはwhileを[NSMutableArray array]返すものとして扱い、 を返すと見なされます。NSMutableArray*[NSArray array]NSArray*

于 2009-12-01T22:17:20.213 に答える
8

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

于 2009-12-01T21:38:48.227 に答える
1

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

于 2011-06-10T20:06:05.070 に答える
1

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

于 2009-12-01T21:41:13.760 に答える