2009-12-01 8 views
11

Cada vez que implemento un método en mi propio código que pueda aceptar o devolver objetos de más de una clase, siempre trato de usar la superclase más específica disponible. Por ejemplo, si fuera a implementar un método que pudiera devolver un NSArray * o un NSDictionary * según su entrada, le daría a ese método un tipo de devolución de NSObject *, ya que esa es la superclase común más directa. He aquí un ejemplo:¿Por qué utilizar (id) en una firma de método cuando (NSObject *) sería más preciso?

@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 donde los usos de Apple (id) en ciertas firmas de método cuando (NSObject *) sería más preciso. Por ejemplo, aquí está un método de 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 devolución de (NSObject *) sería una mejor opción que (id), ya que la persona que llama podría llamar a los métodos de NSObject como retener sin un tipo de conversión.

Generalmente trato de emular los modismos establecidos por los marcos oficiales, pero también me gusta entender qué los motiva. Estoy seguro de que Apple tiene alguna razón válida para usar (id) en casos como este, pero simplemente no lo estoy viendo. ¿Qué me estoy perdiendo?

+0

NSProxy no es un objeto NSObject. NSObject no es la única clase de raíz, por lo que no es una suposición segura de que todo será una subclase de ella. –

Respuesta

8

El uso de id indica al compilador que será un objeto de tipo desconocido. Al usar NSObject, el compilador esperaría que solo usara mensajes disponibles para NSObject. Entonces ... Si sabe que se devolvió una matriz y se fundió como id, puede llamar a objectAtIndex: sin advertencias del compilador. Mientras que regresas con un elenco de NSObject, recibirás advertencias.

1

Ya puede llamar -retain on puninters of type id without casting. Si usa un tipo de superclase específico, deberá lanzar el puntero cada vez que llame al método de una subclase para evitar las advertencias del compilador. Use id para que el compilador no lo advierta y para expresar mejor su intención.

19

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 aleatorio y, por lo tanto, objectAtIndex: devolverá un objeto de cualquier tipo aleatorio. Convertirlo en NSObject* o id <NSObject> sería incorrecto por dos razones; primero, una matriz puede contener subclases no NSObject siempre que implementen un cierto conjunto pequeño de métodos y, en segundo lugar, un tipo de devolución específico requerirá conversión.

(2) Objective-C no admite declaraciones covariantes. Tenga en cuenta:

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

Ahora, puede llamar +array tanto NSArray y NSMutableArray. El primero devuelve una matriz inmutable y la segunda una matriz mutable. Debido a la falta de soporte de declaración covariante de Objective-C, si lo anterior se declaró como devolución (NSArray*), los clientes del método de subclases tendrían que convertir a `(NSMutableArray *). Feo, frágil y propenso a errores. Por lo tanto, usar el tipo genérico es, generalmente, la solución más directa.

Entonces ... si está declarando un método que devuelve una instancia de una clase específica, encasillado explícitamente. Si está declarando un método que será anulado y que la anulación puede devolver una subclase y, el hecho de que devuelva una subclase se expondrá a los clientes, luego use (id).

No es necesario presentar una falla, ya hay varias.


Tenga en cuenta que ObjC ahora ha limitado el apoyo co-varianza a través de la palabra clave instancetype.

I.e. método de arreglos NSArray 's + 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*.

1

(id) también se devuelve con frecuencia para permitir que los objetos se subclases más fácilmente. Por ejemplo, en los métodos de inicialización y conveniencia, return (id) significa que cualquier subclase no tiene que anular los métodos de la superclase a menos que haya una razón específica para hacerlo.

Cuestiones relacionadas