Sim, há benefícios em usar instancetypeem todos os casos em que se aplica. Explicarei com mais detalhes, mas deixe-me começar com esta declaração em negrito: Use instancetypesempre que apropriado, ou seja, sempre que uma classe retornar uma instância dessa mesma classe.
De fato, eis o que a Apple diz agora sobre o assunto:
No seu código, substitua as ocorrências idcomo um valor de retorno por instancetypeonde apropriado. Normalmente, esse é o caso dos initmétodos e métodos de fábrica de classe. Mesmo que o compilador converta automaticamente métodos que começam com "alocar", "init" ou "novo" e têm um tipo de idretorno a ser retornado instancetype, ele não converte outros métodos. A convenção Objective-C é escrever instancetypeexplicitamente para todos os métodos.
Com isso fora do caminho, vamos seguir em frente e explicar por que é uma boa ideia.
Primeiro, algumas definições:
@interface Foo:NSObject
- (id)initWithBar:(NSInteger)bar; // initializer
+ (id)fooWithBar:(NSInteger)bar; // class factory
@end
Para uma fábrica de classe, você deve sempre usar instancetype. O compilador não converte automaticamente idpara instancetype. Esse idé um objeto genérico. Mas se você o fizer, instancetypeo compilador saberá que tipo de objeto o método retornará.
Este não é um problema acadêmico. Por exemplo, [[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData]gerará um erro no Mac OS X ( apenas ) Vários métodos denominados 'writeData:' encontrados com resultado, tipo de parâmetro ou atributos incompatíveis . O motivo é que NSFileHandle e NSURLHandle fornecem um writeData:. Como [NSFileHandle fileHandleWithStandardOutput]retorna um id, o compilador não tem certeza de qual classe writeData:está sendo chamada.
Você precisa contornar isso, usando:
[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];
ou:
NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];
Obviamente, a melhor solução é declarar fileHandleWithStandardOutputcomo retornando um instancetype. Então o elenco ou a tarefa não são necessários.
(Observe que no iOS, este exemplo não produzirá um erro, pois apenas NSFileHandlefornece um writeData:lá. Existem outros exemplos, como length, que retorna um CGFloatde UILayoutSupportmas um NSUIntegerde NSString.)
Nota : Desde que escrevi isso, os cabeçalhos do macOS foram modificados para retornar um em NSFileHandlevez de um id.
Para inicializadores, é mais complicado. Quando você digita isso:
- (id)initWithBar:(NSInteger)bar
… O compilador fingirá que você digitou isso:
- (instancetype)initWithBar:(NSInteger)bar
Isso foi necessário para o ARC. Isso é descrito em Tipos de resultados relacionados ao Clang Language Extensions . É por isso que as pessoas lhe dirão que não é necessário usá-lo instancetype, embora eu afirme que você deveria. O restante desta resposta lida com isso.
Há três vantagens:
- Explícito. Seu código está fazendo o que diz, em vez de outra coisa.
- Padronizar. Você está construindo bons hábitos para tempos que importam e que existem.
- Consistência. Você estabeleceu alguma consistência no seu código, o que o torna mais legível.
Explícito
É verdade que não há benefício técnico em retornar instancetypede um init. Mas isso ocorre porque o compilador converte automaticamente o idpara instancetype. Você está confiando nessa peculiaridade; enquanto você escreve que initretorna um id, o compilador o interpreta como se retornasse um instancetype.
Eles são equivalentes ao compilador:
- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;
Estes não são equivalentes aos seus olhos. Na melhor das hipóteses, você aprenderá a ignorar a diferença e examiná-la. Isso não é algo que você deve aprender a ignorar.
padronizar
Enquanto não há nenhuma diferença com inite outros métodos, não é uma diferença assim que você definir uma fábrica de classe.
Estes dois não são equivalentes:
+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Você quer o segundo formulário. Se você está acostumado a digitar instancetypecomo o tipo de retorno de um construtor, sempre acertará.
Consistência
Por fim, imagine se você juntar tudo: deseja uma initfunção e também uma fábrica de classes.
Se você usar idpara init, você acaba com um código como este:
- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Mas se você usar instancetype, você obtém o seguinte:
- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
É mais consistente e mais legível. Eles retornam a mesma coisa, e agora isso é óbvio.
Conclusão
A menos que você esteja intencionalmente escrevendo código para compiladores antigos, use-o instancetypequando apropriado.
Você deve hesitar antes de escrever uma mensagem que retorne id. Pergunte a si mesmo: isso está retornando uma instância desta classe? Se sim, é um instancetype.
Certamente há casos em que você precisa retornar id, mas provavelmente usará com instancetypemuito mais frequência.