objective c - Inicializando una propiedad, notación de puntos
objective-c initialization (1)
¿Es una mala idea usar la notación de puntos para inicializar retener las propiedades a cero en mis métodos init?
Con cualquier propiedad ordinaria como esta:
@property (nonatomic, retain) id foo;
Digamos que en mi método init establezco self.foo = nil
. El método sintetizado primero libera o autoellama a foo
(no está exactamente seguro de la impedancia subyacente). ¿Es seguro que nunca antes de la primera llamada setter o getter? ¿O señalaría basura al azar a menos que explícitamente establezca foo = nil
sin la notación de puntos?
¿Es una mala idea usar la notación de puntos para inicializar retener las propiedades a cero en mis métodos init?
Sí, es una mala idea.
1) El objeto ya se ha puesto a cero en la secuencia alloc
+ init
, por lo que no es necesario asignarlo nil. En otras palabras, esta llamada es inútil a menos que tenga efectos secundarios en sus accesorios (también se deben evitar los efectos secundarios en los usuarios en esta etapa).
2) No debe dealloc
mensajes por sí mismo con métodos que se anulan en estados parcialmente construidos (por ejemplo, init
y dealloc
).
¿Hay alguna razón para el # 2? A menudo hago self.array = [NSMutableArray array]; en mis métodos init.
La razón es que su objeto no debería estar interesado en el comportamiento de la interfaz de clase durante estados parcialmente construidos ( init...
, dealloc
, finalize
y muchos copyWithZone:
implementaciones). su clase debería estar interesada en inicializar correctamente (como en init...
) y limpiar sus miembros (como en dealloc
) sin introducir efectos secundarios.
Considere este ejemplo, que puede compilar como una herramienta Foundation para OS X:
#import <Foundation/Foundation.h>
enum { UseItTheRightWay = true -OR- false };
@interface MONObjectA : NSObject
{
NSMutableArray * array;
}
@property (nonatomic, retain) NSArray * array;
@end
@implementation MONObjectA
@synthesize array;
- (id)init
{
self = [super init];
if (0 != self) {
NSLog(@"%s, %@",__PRETTY_FUNCTION__, self);
if (UseItTheRightWay) {
array = [NSMutableArray new];
}
else {
self.array = [NSMutableArray array];
}
}
return self;
}
- (void)dealloc
{
NSLog(@"%s, %@",__PRETTY_FUNCTION__, self);
if (UseItTheRightWay) {
[array release], array = nil;
}
else {
self.array = nil;
}
[super dealloc];
}
@end
@interface MONObjectB : MONObjectA
{
NSMutableSet * set;
}
@end
@implementation MONObjectB
- (id)init
{
self = [super init];
if (0 != self) {
NSLog(@"%s, %@",__PRETTY_FUNCTION__, self);
set = [NSMutableSet new];
}
return self;
}
- (void)dealloc
{
NSLog(@"%s, %@",__PRETTY_FUNCTION__, self);
[set release], set = nil;
[super dealloc];
}
- (void)setArray:(NSArray *)arg
{
NSLog(@"%s, %@",__PRETTY_FUNCTION__, self);
NSMutableSet * tmp = arg ? [[NSMutableSet alloc] initWithArray:arg] : nil;
[super setArray:arg];
[set release];
set = tmp;
}
@end
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[[MONObjectB new] release];
/* the tool must be named ''Props'' for this to work as expected, or you can just change ''Props'' to the executable''s name */
system("leaks Props");
[pool drain];
return 0;
}
El cambio principal al comportamiento de alternancia en esta prueba es UseItTheRightWay
.
Si UseItTheRightWay
es verdadero , se nos da el resultado:
2011-05-09 01:52:11.175 Props[45138:a0f] -[MONObjectA init], <MONObjectB: 0x10010c750>
2011-05-09 01:52:11.177 Props[45138:a0f] -[MONObjectB init], <MONObjectB: 0x10010c750>
2011-05-09 01:52:11.179 Props[45138:a0f] -[MONObjectB dealloc], <MONObjectB: 0x10010c750>
2011-05-09 01:52:11.179 Props[45138:a0f] -[MONObjectA dealloc], <MONObjectB: 0x10010c750>
leaks Report Version: 2.0
Process: Props [45138]
< --- snip --- >
Process 45138: 1581 nodes malloced for 296 KB
Process 45138: 0 leaks for 0 total leaked bytes.
Y si UseItTheRightWay
es falso , se nos da el resultado:
2011-05-09 01:55:51.611 Props[45206:a0f] -[MONObjectA init], <MONObjectB: 0x10010c750>
2011-05-09 01:55:51.614 Props[45206:a0f] -[MONObjectB setArray:], <MONObjectB: 0x10010c750>
2011-05-09 01:55:51.615 Props[45206:a0f] -[MONObjectB init], <MONObjectB: 0x10010c750>
2011-05-09 01:55:51.617 Props[45206:a0f] -[MONObjectB dealloc], <MONObjectB: 0x10010c750>
2011-05-09 01:55:51.618 Props[45206:a0f] -[MONObjectA dealloc], <MONObjectB: 0x10010c750>
2011-05-09 01:55:51.618 Props[45206:a0f] -[MONObjectB setArray:], <MONObjectB: 0x10010c750>
leaks Report Version: 2.0
Process: Props [45206]
< --- snip --- >
Process 45206: 1585 nodes malloced for 297 KB
Process 45206: 1 leak for 48 total leaked bytes.
Leak: 0x100110970 size=48 zone: DefaultMallocZone_0x100005000 instance of ''NSCFSet'', type ObjC, implemented in Foundation
0x70294ff8 0x00007fff 0x00001080 0x00000001 .O)p............
0x00000001 0x00000000 0x00000000 0x00010000 ................
0x707612a8 0x00007fff 0x00000000 0x00000000 ..vp............
Problema n. ° 1
La falla obvia de este ejemplo es la fuga, introducida en dealloc
.
Problema n. ° 2
Lo segundo que te morderá es más sutil:
-[MONObjectA init]
-[MONObjectB setArray:]
-[MONObjectB init]
¿¿¿Qué es esto??? -[MONObjectB setArray:]
se llama antes -[MONObjectB init]
? Eso significa que la implementación de MONObjectB
se usa antes -[MONObjectB init]
, e incluso antes -[MONObjectA init]
ha salido. Eso no es bueno = /
Los diseños no triviales producirán un montón de efectos secundarios indeseables, comportamiento extraño, fugas, etc. Los diseños complejos fallarán de maneras muy obvias y muy sutiles que pueden ser muy difíciles de rastrear. es mejor evitar los dolores de cabeza por mantenimiento sobre tales diferencias escritas triviales, y escribir las clases de la manera correcta desde el principio (a pesar de que podría alejarse haciendo esto por bastante tiempo, sin efectos secundarios obvios).