Является ли плохой идеей использование точечной нотации для инициализации сохранения свойств для nil в моих методах инициализации?
Да, это плохая идея.
1) Объект уже обнулен в последовательности alloc
+ init
, поэтому нет необходимости присваивать ему значение nil.Другими словами, этот вызов бесполезен, если только у вас нет побочных эффектов в аксессорах (на этой стадии также следует избегать побочных эффектов в аксессорах).
2) Вы должны не сообщать о себе сметоды, которые переопределяются в частично построенных состояниях (например, init
и dealloc
).
Есть ли причина для # 2?Я часто делаю self.array = [NSMutableArray array];в моих методах init.
Причина в том, что ваш объект не должен интересоваться поведением интерфейса класса во время частично сконструированных состояний (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]
.Это нехорошо = \
Нетривиальные проекты просто вызовут кучу нежелательных побочных эффектов, странного поведения, утечек и так далее.Сложные проекты потерпят неудачу очень очевидным и очень тонким способом, который может быть очень трудно отследить.Лучше избегать головной боли от обслуживания из-за таких тривиальных письменных различий и правильно писать классы с самого начала (даже если вы можете делать это довольно долго, без явных побочных эффектов).