2009-12-01 8 views
11

Immer, wenn ich eine Methode in meinem eigenen Code implementiere, die Objekte von mehr als einer Klasse akzeptieren oder zurückgeben kann, versuche ich immer die spezifischste Oberklasse zu verwenden. Wenn ich zum Beispiel eine Methode implementieren würde, die abhängig von ihrer Eingabe ein NSArray * oder ein NSDictionary * zurückgibt, würde ich dieser Methode den Rückgabetyp NSObject * geben, da dies die direkteste gemeinsame Oberklasse ist. Hier ein Beispiel:Warum (ID) in einer Methodensignatur verwenden, wenn (NSObject *) genauer wäre?

@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 

ich viele Fälle in Foundation und andere APIs, wo Apple-Anwendungen (id) in bestimmten Methodensignaturen, wenn (NSObject *) wären genauer bemerkt habe. Zum Beispiel ist hier ein Verfahren zur Herstellung NSPropertyListSerialization:

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

Die möglichen Rückgabetypen von dieser Methode sind NSData, NSString, NSArray, NSDictionary, NSDate und NSNumber. Es scheint mir, dass ein Rückgabetyp von (NSObject *) eine bessere Wahl wäre als (id), da der Aufrufer dann in der Lage wäre, NSObject-Methoden wie Retain ohne eine Typumwandlung aufzurufen.

Ich versuche generell, die Idiome zu kopieren, die von den offiziellen Frameworks eingeführt werden, aber ich mag auch zu verstehen, was sie motiviert. Ich bin mir sicher, dass Apple in solchen Fällen einen gültigen Grund für die Verwendung von (id) hat, aber ich sehe es einfach nicht. Was vermisse ich?

+0

NSProxy ist kein NSObject. NSObject ist nicht die einzige Root-Klasse, daher ist es keine sichere Annahme, dass alles eine Unterklasse davon ist. –

Antwort

8

Mit der ID wird dem Compiler mitgeteilt, dass es sich um ein unbekanntes Objekt handelt. Mit NSObject würde der Compiler dann erwarten, dass Sie nur Nachrichten verwenden, die für NSObject verfügbar sind. Also ... Wenn Sie wissen, dass ein Array zurückgegeben wurde und als ID übergeben wurde, können Sie objectAtIndex aufrufen: ohne Compiler-Warnungen. Während Sie mit einem NSObject-Cast zurückkehren, erhalten Sie Warnungen.

1

Sie können bereits auf Zeiger des Typs ID ohne Casting aufrufen. Wenn Sie einen bestimmten Oberklasse-Typ verwenden, müssen Sie den Zeiger jedes Mal umwandeln, wenn Sie die Methode einer Unterklasse aufrufen, um Compiler-Warnungen zu vermeiden. Verwenden Sie die ID, damit der Compiler Sie nicht warnt und Ihre Absicht besser anzeigt.

19

Der Grund, warum (id) in Methodendeklarationen verwendet wird, ist zweifach:

(1) Das Verfahren jede Art nehmen oder zurückkehren. NSArray enthält ein beliebiges Objekt, und objectAtIndex: gibt ein Objekt beliebigen Typs zurück. Casting es zu NSObject* oder id <NSObject> würde aus zwei Gründen falsch sein; Erstens kann ein Array nicht-NSObject-Unterklassen enthalten, solange sie einen bestimmten kleinen Satz von Methoden implementieren, und zweitens würde ein bestimmter Rückgabetyp ein Casting erfordern.

(2) Objective-C unterstützt kovariante Deklarationen nicht. Bedenken Sie:

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

Nun Sie +array sowohl auf NSArray und NSMutableArray aufrufen können. Der erste gibt ein unveränderliches Array zurück und das zweite ein veränderbares Array. Aufgrund der fehlenden Unterstützung für kovariante Deklarationen von Objective-C müssten die Clients der Methode "subclasses" in "(NSMutableArray *)" umgewandelt werden, wenn die obigen Anweisungen als (NSArray*) zurückgegeben wurden. Hässlich, zerbrechlich und fehleranfällig. Daher ist die Verwendung des generischen Typs im Allgemeinen die einfachste Lösung.

Also ... Wenn Sie eine Methode deklarieren, die eine Instanz einer bestimmten Klasse zurückgibt, typecast explizit. Wenn Sie eine Methode deklarieren, die überschrieben wird und die Überschreibung eine Unterklasse und zurückgibt, wird die Tatsache, dass eine Unterklasse zurückgegeben wird, Clients angezeigt, und verwenden Sie dann (id).

Keine Notwendigkeit, einen Fehler zu archivieren - es gibt bereits einige.


anzumerken, dass ObjC jetzt beschränkt Kovarianz Unterstützung durch das Schlüsselwort instancetype hat.

I.e. NSArray ‚s + Array-Verfahren nun erklärt werden könnte, wie:

+ (instancetype) array; 

Und der Compiler würde [NSMutableArray array] behandeln, als ein NSMutableArray* während [NSArray array] Rückkehr würde als Rückkehr NSArray* in Betracht gezogen werden.

1

(ID) wird auch oft zurückgegeben, damit Objekte leichter in Unterklassen umgewandelt werden können. In Initialisierungs- und Convenience-Methoden bedeutet das Zurückgeben (id) beispielsweise, dass jede Unterklasse die Methoden der Oberklasse nicht außer Kraft setzen muss, es sei denn, es gibt einen bestimmten Grund dafür.

Verwandte Themen