Perché usare (id) in una firma del metodo quando (NSObject *) sarebbe più preciso?

StackOverflow https://stackoverflow.com/questions/1829028

  •  11-09-2019
  •  | 
  •  

Domanda

Ogni volta che implementare un metodo nel mio codice che può accettare o restituire gli oggetti di più di una classe, cerco sempre di usare la superclasse più specifica disponibile. Ad esempio, se dovessi implementare un metodo che potrebbe restituire un NSArray * o un NSDictionary * a seconda del suo ingresso, darei questo metodo un tipo di ritorno NSObject *, dato che è il superclasse comune più diretto. Ecco un esempio:

@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

ho notato molti casi in Fondazione e altre API in cui Apple utilizza (id) in alcune firme del metodo quando (NSObject *) sarebbe più preciso. Ad esempio, ecco un metodo di NSPropertyListSerialization:

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

I possibili tipi di ritorno di questo metodo sono NSData, NSString, NSArray, NSDictionary, NSDate e NSNumber. Mi sembra che un tipo di ritorno di (NSObject *) sarebbe una scelta migliore di (id), dal momento che il chiamante sarebbe quindi in grado di chiamare i metodi NSObject come mantenere senza un tipo-cast.

Io in genere cerco di emulare i modi di dire stabiliti dai quadri ufficiali, ma mi piace anche di capire che cosa li motiva. Sono sicuro che Apple ha qualche motivo valido per l'utilizzo (id) in casi come questo, ma io non sto vedendo. Che cosa mi manca?

È stato utile?

Soluzione

Utilizzando id dice al compilatore sarà un oggetto di tipo sconosciuto. Utilizzando NSObject il compilatore avrebbe poi si aspettano che tu sia solo utilizzando i messaggi a disposizione NSObject. Quindi ... Se si conosce un array è stato restituito ed è colato come id, è possibile chiamare objectAtIndex: senza avvisi del compilatore. Mentre tornando con un cast di NSObject, si otterrà avvertimenti.

Altri suggerimenti

Il motivo per cui (id) è utilizzato in dichiarazioni di metodo è duplice:

(1) Il metodo può prendere o restituire qualsiasi tipo. NSArray contiene qualsiasi oggetto casuale e, quindi, objectAtIndex: restituirà un oggetto di qualsiasi tipo casuale. Casting a NSObject* o id <NSObject> sarebbe corretto per due motivi; primo, un array può contenere sottoclassi non NSObject fintanto che implementano un certo piccolo insieme di metodi e, dall'altro, un tipo specifico di ritorno richiederebbero colata.

(2) Objective-C non supporta dichiarazioni covarianti. Prendere in considerazione:

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

Ora, è possibile chiamare +array sia NSArray e NSMutableArray. Il primo restituisce un array immutabile e quest'ultimo una matrice mutevole. A causa della mancanza di sostegno dichiarazione covariante di Objective-C, se quanto sopra sono stati dichiarati in modo da restituire (NSArray*), i clienti del metodo sottoclassi avrebbero dovuto lanciare a `(NSMutableArray *). Brutto, fragile e soggetto a errori. Pertanto, utilizzando il tipo generico è, generalmente, la soluzione più semplice.

Quindi ... se si dichiara un metodo che restituisce un'istanza di una classe specifica, typecast esplicito. Se si dichiara un metodo che sarà ignorata e che override di restituire una sottoclasse e il fatto che restituisce una sottoclasse sarà esposto ai clienti, quindi utilizzare (id).

Non c'è bisogno di aprire un bug -. Ci sono diversi già


Si noti che objC ora ha un supporto limitato co-varianza attraverso la parola instancetype.

vale a dire. il metodo array + di NSArray potrebbe ora essere dichiarato come:

+ (instancetype) array;

E il compilatore tratterebbe [NSMutableArray array] come restituzione di un NSMutableArray* mentre [NSArray array] sarebbe stato considerato come il ritorno NSArray*.

Si può già chiamare -retain su puntatori di tipo id senza proiettare. Se si utilizza un tipo superclasse specifica, dovrete lanciare il puntatore ogni volta che si chiama il metodo di una sottoclasse al fine di evitare avvisi del compilatore. Utilizzare id in modo che il compilatore non avvisa e per significare meglio il vostro intento.

(id) è anche spesso tornato al fine di consentire oggetti da sottoclasse più facilmente. Per esempio, in modalità di inizializzazione e di convenienza, di ritorno (id) significa che ogni sottoclasse non deve sovrascrivere i metodi della superclasse a meno che non ci sia un motivo specifico per farlo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top