Inicializadores designados iOS: utilizando NS_DESIGNATED_INITIALIZER
objective-c xcode (3)
Tenemos esta nueva macro introducida en XCode 6: NS_DESIGNATED_INITIALIZER
Busqué en la red, pero no pude encontrar ninguna buena documentación sobre cómo usar esto.
Sintácticamente, podemos usarlo como:
- (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
Pero, ¿cuáles son las posibles ventajas de marcar un inicializador con esta macro y también cuáles son las cosas que deberíamos considerar al usar esto?
Estoy principalmente interesado en los casos de uso de esta macro. Cualquier enlace / documentación sería apreciado.
El uso de NS_DESIGNATED_INITIALIZER
está muy bien explicado en http://useyourloaf.com/blog/2014/08/19/xcode-6-objective-c-modernization.html :
El inicializador designado garantiza que el objeto esté completamente inicializado al enviar un mensaje de inicialización a la superclase. El detalle de implementación se vuelve importante para un usuario de la clase cuando lo subclase. Las reglas para los inicializadores designados en detalle:
- Un inicializador designado debe llamar (a través de super) un inicializador designado de la superclase. Donde NSObject es la superclase, esto es solo [superinicio].
- Cualquier iniciador de conveniencia debe llamar a otro inicializador de la clase, lo que finalmente conduce a un inicializador designado.
- Una clase con inicializadores designados debe implementar todos los inicializadores designados de la superclase.
Como ejemplo, si su interfaz es
@interface MyClass : NSObject
@property(copy, nonatomic) NSString *name;
-(instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
-(instancetype)init;
@end
a continuación, el compilador comprueba si el iniciador (de conveniencia) init
llama al inicializador (designado) initWithName:
, por lo que esto provocaría una advertencia:
-(instancetype)init
{
self = [super init];
return self;
}
y esto estaría bien:
-(instancetype)init
{
self = [self initWithName:@""];
return self;
}
En Swift, las reglas sobre inicializadores designados y convenientes son aún más estrictas, y si mezcla el código Objective-C y Swift, marcar los inicializadores designados de Objective-C ayuda al compilador a aplicar las reglas.
Por ejemplo, esta subclase Swift causaría un error de compilación:
class SwClass: MyClass {
var foo : String
init(foo : String) {
self.foo = foo
super.init()
}
}
y esto estaría bien:
class SwClass: MyClass {
var foo : String
init(foo : String) {
self.foo = foo
super.init(name: "")
}
}
Los inicializadores designados definen cómo estructuramos nuestros inicializadores al crear subclases; son el "inicializador canónico" para su clase. Garantizó ser confiable independientemente del inicializador designado en la cadena de superclase que usted llame, y siempre irá desde el ancestro más lejano hasta el descendiente más lejano.
Un inicializador designado no define qué inicializador debe usar al crear un objeto. Está muy explicado en https://blog.twitter.com/2014/how-to-objective-c-initializer-patterns .
Mi forma más común de hacer esto:
@interface Person : NSObject
- (nullable instancetype)initWithName:(nonnull NSString *)name NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)init NS_UNAVAILABLE;
@property (nonatomic, nonnull) NSString *name;
@end
E implementación
@implementation Person
- (instancetype)initWithName:(NSString *)name
{
self = [super init];
if (self) {
self.name = name;
}
return self;
}
@end
En este caso, no debe anular NS_DESIGNATED_INITIALIZER
de su método de superclase ( init:
NSObject
init:
en este caso) - usamos NS_UNAVAILABLE
para marcar este método como innecesario. O puede anularlo para llamar a su inicializador designado con los parámetros predeterminados.