Инициализация свойства, точечная запись
Это плохая идея использовать точечную нотацию для инициализации сохранения свойств в ноль в моих методах инициализации?
С любой обычной собственностью, подобной этой:
@property (nonatomic, retain) id foo;
Скажи в моем методе инициализации я установил self.foo = nil
, Синтезированный метод первых выпусков или аутрелизов foo
(не совсем уверен в основной имплементации). Является foo
гарантированно будет равен нулю до первого вызова сеттера или геттера? Или это будет указывать на случайный мусор, если я не установил явно foo = nil
без точечной записи?
1 ответ
Это плохая идея использовать точечную нотацию для инициализации сохранения свойств в ноль в моих методах инициализации?
Да, это плохая идея.
1) Объект уже обнулен в alloc
+init
последовательность, поэтому нет необходимости присваивать ей ноль. Другими словами, этот вызов бесполезен, если только у вас нет побочных эффектов в аксессорах (на этой стадии также следует избегать побочных эффектов в аксессорах).
2) Вы не должны сообщать себя с помощью методов, которые переопределяются в частично сконструированных состояниях (например, init
а также dealloc
).
Есть ли причина для № 2? Я часто делаю self.array = [NSMutableArray array]; в моих методах инициализации.
Причина в том, что ваш объект не должен интересоваться поведением интерфейса класса во время частично сконструированных состояний (init...
, dealloc
, finalize
, и много copyWithZone:
реализации). Ваш класс должен быть заинтересован в правильной инициализации (как в init...
) и очистки после себя, включая его членов (как в dealloc
без побочных эффектов.
рассмотрите этот пример, который вы можете построить как инструмент Foundation для 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;
}
Основным переключателем для переключения поведения в этом тесте является UseItTheRightWay
,
Если UseItTheRightWay
верно, нам дан результат:
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.
И если UseItTheRightWay
ложно, нам дают результат:
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............
Проблема № 1
Очевидная ошибка этого примера - утечка, введенная в dealloc
,
Проблема № 2
Второе, что вас укусит, это более тонко:
-[MONObjectA init]
-[MONObjectB setArray:]
-[MONObjectB init]
Что это??? -[MONObjectB setArray:]
называется раньше -[MONObjectB init]
? Это означает, что MONObjectB
Реализация используется раньше -[MONObjectB init]
и даже раньше -[MONObjectA init]
вышел. Это не хорошо =\
Нетривиальные дизайны будут вызывать нежелательные побочные эффекты, странное поведение, утечки и так далее. Сложные проекты потерпят неудачу очень очевидным и очень тонким способом, который может быть очень трудно отследить. Лучше избегать головной боли от обслуживания из-за таких тривиальных письменных различий и правильно писать классы с самого начала (даже если вы можете делать это довольно долго, без явных побочных эффектов).