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