¿Por qué utilizar (id) en una firma de método cuando (NSObject *) sería más preciso?

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

  •  11-09-2019
  •  | 
  •  

Pregunta

Siempre que implementar un método en mi propio código que puede aceptar o devolver los objetos de más de una clase, siempre trato de usar la superclase más específico disponible. Por ejemplo, si yo fuera a poner en práctica un método que podría devolver un NSArray * o un NSDictionary * dependiendo de su entrada, le daría ese método un tipo de retorno de NSObject *, ya que es la superclase común más directa. He aquí un ejemplo:

@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

Me he dado cuenta de muchos casos en Fundación y otras API que Apple utiliza (id) en ciertas firmas de método cuando (NSObject *) sería más preciso. Por ejemplo, aquí hay un método para NSPropertyListSerialization:

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

Los posibles tipos de retorno de este método son NSData, NSString, NSArray, NSDictionary, NSDate, y NSNumber. Me parece que un tipo de retorno de (NSObject *) sería una mejor opción que (id), ya que la persona que llama entonces sería capaz de llamar a los métodos NSObject como mantener sin un tipo de fundición.

Yo por lo general trato de emular a los idiomas establecidos por los marcos oficiales, pero también me gusta entender lo que les motiva. Estoy seguro de que Apple tiene alguna razón válida para el uso de (id) en casos como este, pero yo no lo veo. ¿Qué me falta?

¿Fue útil?

Solución

El uso de Identificación le dice al compilador que será un objeto de tipo desconocido. Usando el compilador NSObject entonces sería espera que seas solamente por medio de mensajes disponibles a NSObject. Así que ... Si conoces se devuelve una matriz y se está fundido como ID, puede llamar objectAtIndex: sin advertencias del compilador. Mientras que volviendo con un elenco de NSObject, obtendrá advertencias.

Otros consejos

La razón por la (id) se utiliza en las declaraciones de método es doble:

(1) El método puede tomar o devolver cualquier tipo. NSArray contiene cualquier objeto al azar y, por tanto, objectAtIndex: devolverá un objeto de cualquier tipo aleatorio. Echándola a NSObject* o id <NSObject> sería incorrecto por dos razones; primero, una matriz puede contener subclases no NSObject siempre que implementan un cierto pequeño conjunto de métodos y, en segundo lugar, un tipo de retorno específico requerirían de colada.

(2) Objetivo-C no soporta declaraciones covariantes. Considere lo siguiente:

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

Ahora, puede llamar +array tanto NSArray y NSMutableArray. El antiguo devuelve una matriz inmutable y el último un array mutable. Debido a la falta de apoyo de declaración covariante de Objective-C, si lo anterior fueron declarados como devolver (NSArray*), los clientes del método subclases tendrían que echar a `(NSMutableArray *). Feo, frágil y propenso a errores. Por lo tanto, utilizando el tipo genérico es, en general, la solución más sencilla.

Así que ... si usted está declarando un método que devuelve una instancia de una clase específica, encasillado de forma explícita. Si declara un método que será anulado y que de anulación puede devolver una subclase y el hecho de que devuelve una subclase estará expuesto a los clientes, a continuación, utilizar (id).

No hay necesidad de presentar un error -. Ya hay varios


Tenga en cuenta que ahora tiene soporte ObjC covarianza limitado a través de la palabra clave instancetype.

es decir. Método gama de + NSArray ahora podría ser declarado como:

+ (instancetype) array;

Y el compilador trataría [NSMutableArray array] como devolver un NSMutableArray* mientras [NSArray array] sería considerado como devolver NSArray*.

Ya se puede llamar -retain sobre los punteros de tipo ID sin poner. Si utiliza un tipo específico de superclase, que tendrá que emitir el puntero cada vez que se llama el método de una subclase con el fin de evitar las advertencias del compilador. Identificación utilizar lo que el compilador no le avisará y para indicar mejor su intención.

(id) es también a menudo de regresar a fin de que los objetos a una subclase más fácilmente. Por ejemplo, en los métodos de inicializador y de conveniencia, volviendo (id) significa que cualquier subclase no tiene que reemplazar los métodos de la superclase a menos que haya una razón específica para hacerlo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top