Por que usar (id) em uma assinatura de método quando (NSObject *) seria mais preciso?

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

  •  11-09-2019
  •  | 
  •  

Pergunta

Sempre que eu implementar um método no meu próprio código que pode aceitar ou objetos de retorno de mais de uma classe, eu sempre tentar usar o mais específico superclasse disponível. Por exemplo, se eu estivesse indo para implementar um método que pode retornar um NSArray * ou um NSDictionary * dependendo de sua entrada, eu daria esse método um tipo de retorno de NSObject *, já que é a superclasse comum mais direta. Aqui está um exemplo:

@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

Eu observei muitos casos em Fundação e outras APIs quando as utilizações da Apple (ID) em determinadas assinaturas de método quando (NSObject *) seria mais preciso. Por exemplo, aqui está um método de NSPropertyListSerialization:

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

Os possíveis tipos de retorno deste método são NSData, NSString, NSArray, NSDictionary, NSDate e NSNumber. Parece-me que um tipo de retorno (NSObject *) seria uma escolha melhor do que (id), uma vez que o autor da chamada, então, seria capaz de chamar métodos NSObject como manter sem um tipo de-cast.

Eu geralmente tentam imitar as expressões idiomáticas estabelecidos pelas estruturas oficiais, mas também gosto de entender o que os motiva. Tenho certeza de que a Apple tem alguma razão válida para usar (id) em casos como este, mas eu não estou vendo isso. O que eu estou ausente?

Foi útil?

Solução

Usando id informa ao compilador que vai ser um objeto de tipo desconhecido. Usando NSObject o compilador, então, espero que você esteja usando apenas mensagens disponíveis para NSObject. Então ... Se você conhece um array foi devolvido e está escalado como id, você pode chamar objectAtIndex: sem avisos do compilador. Considerando retornar com um elenco de NSObject, você vai ter avisos.

Outras dicas

A razão pela qual (ID) é utilizado em declarações de método é dupla:

(1) O método pode pegar ou devolver qualquer tipo. NSArray contém qualquer objeto aleatória e, deste modo, objectAtIndex: retornará um objecto de qualquer tipo aleatório. Lançando-a NSObject* ou id <NSObject> seria incorreto por duas razões; em primeiro lugar, uma matriz pode conter subclasses não NSObject enquanto eles implementar um certo pequeno conjunto de métodos e, por outro, um tipo de retorno específica exigiria casting.

(2) Objective-C não suporta declarações covariantes. Considere o seguinte:

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

Agora, você pode chamar +array em ambos NSArray e NSMutableArray. Os antigos retorna uma matriz imutável e a uma matriz mutável último. Por causa da falta de apoio declaração covariante de Objective-C, se o acima foram declaradas como retornando (NSArray*), os clientes do método subclasses teria de elenco para `(NSMutableArray *). Feio, frágil e propenso a erros. Assim, usando o tipo genérico é, em geral, a solução mais simples.

Então ... se você está declarando um método que retorna uma instância de uma classe específica, typecast explicitamente. Se você está declarando um método que será substituído e que a substituição pode retornar uma subclasse e o fato de que ele retorna uma subclasse será exposto aos clientes, em seguida, usar (id).

Não há necessidade de registrar um bug - existem vários já

.

Note que ObjC agora tem suporte co-variância limitada através da palavra-chave instancetype.

i. Método + variedade de NSArray poderia agora ser declarado como:

+ (instancetype) array;

E o compilador iria tratar [NSMutableArray array] como retornando um NSMutableArray* enquanto [NSArray array] seria considerado como retornando NSArray*.

Você já pode chamar -retain em ponteiros do tipo id sem lançar. Se você usar o tipo de uma específica superclasse, você vai ter que converter o ponteiro toda vez que você chamar o método de uma subclasse, a fim de evitar os avisos do compilador. Use id para que o compilador não irá avisá-lo e para melhor significar sua intenção.

(id) também é muitas vezes voltou a fim de permitir objetos para ser uma subclasse com mais facilidade. Por exemplo, em métodos inicializador e conveniência, retornando meios (id) que qualquer subclasse não tem que substituir os métodos da superclasse a menos que haja uma razão específica para fazê-lo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top