Sí, hay beneficios de usar instancetypeen todos los casos donde se aplica. Explicaré con más detalle, pero permítanme comenzar con esta declaración en negrita: useinstancetype siempre que sea apropiado, que es cuando una clase devuelve una instancia de esa misma clase.
De hecho, esto es lo que Apple dice ahora sobre el tema:
En su código, reemplace las ocurrencias de idcomo valor de retorno por instancetypedonde corresponda. Este suele ser el caso de los initmétodos y métodos de clase de fábrica. Aunque el compilador convierte automáticamente los métodos que comienzan con "alloc", "init" o "new" y tienen un tipo de idretorno de return instancetype, no convierte otros métodos. La convención Objective-C es escribir instancetypeexplícitamente para todos los métodos.
Con eso fuera del camino, sigamos adelante y expliquemos por qué es una buena idea.
Primero, algunas definiciones:
@interface Foo:NSObject
- (id)initWithBar:(NSInteger)bar; // initializer
+ (id)fooWithBar:(NSInteger)bar; // class factory
@end
Para una fábrica de clase, siempre debe usar instancetype. El compilador no se convierte automáticamente ida instancetype. Ese ides un objeto genérico. Pero si lo haces uninstancetype compilador, sabe qué tipo de objeto devuelve el método.
Este no es un problema académico. Por ejemplo, [[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData]generará un error en Mac OS X ( solo ) Múltiples métodos llamados 'writeData:' encontrados con resultados no coincidentes, tipo de parámetro o atributos . La razón es que tanto NSFileHandle como NSURLHandle proporcionan a writeData:. Como [NSFileHandle fileHandleWithStandardOutput]devuelve un id, el compilador no está seguro de qué clasewriteData: se está llamando.
Necesita solucionar esto, usando:
[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];
o:
NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];
Por supuesto, la mejor solución es declarar fileHandleWithStandardOutputque devuelve uninstancetype . Entonces el reparto o la tarea no es necesaria.
(Tenga en cuenta que en iOS, este ejemplo no producirá un error, ya que solo NSFileHandleproporciona un writeData:allí. Existen otros ejemplos, como, por ejemplo length, que devuelve un CGFloatde UILayoutSupportpero un NSUIntegerde NSString).
Nota : desde que escribí esto, los encabezados de macOS se han modificado para devolver un en NSFileHandlelugar de un id.
Para los inicializadores, es más complicado. Cuando escribes esto:
- (id)initWithBar:(NSInteger)bar
... el compilador fingirá que escribiste esto en su lugar:
- (instancetype)initWithBar:(NSInteger)bar
Esto era necesario para ARC. Esto se describe en Extensiones de lenguaje Clang Tipos de resultados relacionados . Es por eso que la gente le dirá que no es necesario usarlo instancetype, aunque yo afirmo que debería hacerlo. El resto de esta respuesta trata de esto.
Hay tres ventajas:
- Explícito. Su código está haciendo lo que dice, en lugar de otra cosa.
- Patrón. Estás creando buenos hábitos para los tiempos que importan, que existen.
- Consistencia. Ha establecido cierta consistencia en su código, lo que lo hace más legible.
Explícito
Es cierto que no hay ningún beneficio técnico al regresar instancetypede un init. Pero esto se debe a que el compilador convierte automáticamente el ida instancetype. Estás confiando en este capricho; mientras escribe que initdevuelve un id, el compilador lo interpreta como si devolviera uninstancetype .
Estos son equivalentes al compilador:
- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;
Estos no son equivalentes a tus ojos. En el mejor de los casos, aprenderá a ignorar la diferencia y pasarla por alto. Esto no es algo que debas aprender a ignorar.
Patrón
Si bien no hay ninguna diferencia con initotros métodos, no es una diferencia tan pronto como se define un generador de clases.
Estos dos no son equivalentes:
+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Quieres la segunda forma. Si está acostumbrado a escribir instancetypecomo el tipo de retorno de un constructor, siempre lo hará bien.
Consistencia
Finalmente, imagina si lo pones todo junto: quieres una initfunción y también una fábrica de clase.
Si usas idpara init, terminas con un código como este:
- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Pero si usas instancetype, obtienes esto:
- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
Es más consistente y más legible. Vuelven lo mismo, y ahora eso es obvio.
Conclusión
A menos que esté escribiendo código intencionalmente para compiladores antiguos, debe usarlo instancetypecuando sea apropiado.
Debes dudar antes de escribir un mensaje que regrese id. Pregúntese: ¿Esto devuelve una instancia de esta clase? Si es así, es un instancetype.
Ciertamente, hay casos en los que necesita regresar id, pero probablemente lo usará con instancetypemucha más frecuencia.