Property initialization, dot notation

Is it good to use textual notation to initialize stored properties to nil in my init methods?

With any common property:

@property (nonatomic, retain) id foo; 

Say in my init method, I set self.foo = nil . The synthesized method first releases or auto-implements foo (not quite sure of the underlying impediment). Is foo guarantee to be null before the first call to the setter or getter? Or does it point to random garbage unless I explicitly set foo = nil without dot notation?

+20
initialization properties objective-c reference-counting dealloc
May 9 '11 at 4:55
source share
1 answer

Is it good to use textual notation to initialize stored properties to nil in my init methods?

Yes, this is a bad idea.

1) The object is already nullified in the sequence alloc + init , so there is no need to assign it a zero. In other words, this call is useless if you do not have side effects in your accessories (side effects in accessories should be avoided at this point).

2) You should not communicate with methods that are overridden in partially constructed states (for example, init and dealloc ).

Is there a reason for No. 2? I often do self.array = [NSMutableArray array]; in my init methods.

The reason is that your object should not be interested in the behavior of the class interface during partially constructed states ( init... , dealloc , finalize and many copyWithZone: implementations. your class should be interested in properly initializing (as in init... ) and cleaning after itself, including its members (as in dealloc ) without introducing side effects.

consider this example, which you can create as a Foundation tool for 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; } 

The main switch to switch the mode in this test is UseItTheRightWay .

If UseItTheRightWay true, we are given the result:

 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. 

And if UseItTheRightWay is false, we are given the result:

 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............ 

Problem number 1

In this example, the obvious failure is the leak introduced in dealloc .

Problem number 2

The second thing that will bite you is more subtle:

 -[MONObjectA init] -[MONObjectB setArray:] -[MONObjectB init] 

What is it??? -[MONObjectB setArray:] is called before -[MONObjectB init] ? This means that the MONObjectB implementation MONObjectB used before -[MONObjectB init] and even before the exit -[MONObjectA init] . This is not good = \

Nontrivial constructs will just create a bunch of unwanted side effects, strange behavior, leaks, etc. Complex designs fail in very obvious and very subtle ways that can be very difficult to track. it’s better to avoid the maintenance headache over such minor written differences and write classes correctly from the start (although you could have avoided this for quite some time, without obvious side effects).

+72
May 9 '11 at 5:04 am
source share



All Articles