24

I've heard now from several sources (stackoverflow.com, cocoa-dev, the documentation, blogs, etc) that it is "wrong" to use accessors and settings (foo, setFoo:) in your init and dealloc methods. I understand that there is there is a remote possibility of confusing other objects that are observing the property if you do so. (a simple example is given here)

However, I have to say that I don't agree with this practice for the following reason:

The new Objective-C runtime (the one on the iPhone and the 64-bit runtime in 10.5) allows you to declare properties without declaring a corresponding ivar. For example, the following class will compile just fine on 10.5 or for the iPhone (device, not simulator):

@interface Foo : NSObject { }

  @property (retain) id someObject;

@end

@implementation Foo

  @synthesize someObject;

@end

Understanding that the above is a perfectly valid Objective-C class, let's say I decide to write an initializer, and for memory management purposes, a dealloc method (since GC is not available on the iPhone). Everything I've ever read about initializers and deallocation would lead me to write the following two methods:

- (id) init {
  if (self = [super init]) {
    //initialize the value of someObject to nil
    [self setSomeObject:nil];
  }
  return self;
}

- (void) dealloc {
  //setting someObject to nil will release the previous value
  [self setSomeObject:nil];
  [super dealloc];
}

However, according to the documentation and popular opinion, this is "wrong". So my questions are this:

  1. How am I supposed to initialize someObject without using the accessor? You might say that the compiler (or runtime or whatever) will ensure that someObject is already set to nil, but I believe it would be improper behavior to rely on that. Having a decent background in C, I've seen a fair number of bugs due to not properly initializing variables, and this seems little different.
  2. How can I release someObject if I'm not supposed to use the accessor in the dealloc method?

If the answer to either of these is "you can't", then how can it be bad to use accessors in your init and dealloc methods?

Community
  • 1
  • 1
Dave DeLong
  • 239,073
  • 58
  • 443
  • 495

2 Answers2

9

EDIT (Feb 13, 2013): As noted in my comment below, and especially since the addition of ARC, I've changed my mind on this. Prior to ARC, I saw a lot of crash-causing bugs due to incorrect ivar assignments in init. IMO, especially working with junior teams, the rare problems with using accessors in init were outweighed by the common bugs of ivar access. Since ARC has eliminated these kinds of bugs, the rare-but-possible bugs that using accessor in init can cause are more important, and so I've switched to supporting the direct use of ivars in init and dealloc, and only in those places; accessors everywhere else that is possible (obviously you can't use accessors inside of the accessor itself....)


PRE-ARC answer

I strongly disagree with those who object to accessors in -init. In almost all cases this is a very good place to use accessors, and it saves a lot of bugs I've seen in new Cocoa coders who invariably fail to retain when assigning in -init.

-dealloc is a tougher call. I have a natural inclination to use accessors there (so that they are used everywhere), but it can cause headaches due to KVO (or even NSNotifications if you post a change notification in your setter). That said, while I don't use accessors in -dealloc, I consider it very debatable and Apple is very inconsistent about it (we know they're calling setView: in UIViewController's -dealloc for instance).

In any case, I would say that under-use of accessors has caused 100x the bugs of over-use. I would always err towards using them except when there is a strong reason not to.

Rob Napier
  • 250,948
  • 34
  • 393
  • 528
  • 1
    I consider the benefit of having balanced retain/release in init/dealloc to be important, that's one reason I'm against using accessors in `init`. – Georg Schölly Nov 08 '09 at 21:29
  • 4
    You only get this balance if every retain-requiring ivar is assigned in init. It is not uncommon to have ivars that remain nil after init (lazy-load objects do this in particular, and are a very important performance optimization). So I don't believe you can practically maintain this balance without forbidding important implementation approaches. – Rob Napier Nov 09 '09 at 15:45
  • 6
    A year later and I have a softening in my position. There can be a danger in accessors in -init. If a subclass overrides the accessor and the superclass's -init calls it, the object may be in an inconsistent state. The problem is that it's not possible to know if your subclass is going to do this. Whether this is a real possibility depends on your environment and whether there are different groups who write the superclasses and subclasses. Making this rule dramatically reduced crashes among our jr developers, and we have never encountered the above problem. But there is a possible danger. – Rob Napier Oct 12 '10 at 02:50
  • 3
    I know this thread has been idle for quite a while, but it's worth noting that accessors can help maintain the validity of your code even if the memory management semantics of a property changes. This is mainly relevant in `dealloc`, where `self.var = nil;` will be valid even if `var` is assigned and not retained, while `[var release];` would not be. – Justin Spahr-Summers Jan 02 '11 at 23:50
  • 1
    @Rob Napier Regarding the inconsistency of the object, I'd argue that an object should always be in a semi-consistent state once allocated. In other words, every object should behave reasonably if any of its properties are zero-initialized. If this doesn't hold true, that's the only issue with using accessors in `init`, as construction is more robust than in C++, where subclasses can be in a completely undefined state. – Justin Spahr-Summers Jan 02 '11 at 23:52
  • 1
    @justin and @rob make huge points ... if I have property accessors for an ivar, I always use self.thing = nil; in the dealloc, that way if it's retained it takes care of it, and if it's assigned it takes care of it too. I'm very wary of folks who blindly call release on an ivar in dealloc ... . – Greg Combs Jun 23 '11 at 19:29
8

I understand that the current 10.5 behavior under which the synthesized ivars are not directly accessible is considered by Apple to be a bug; you should be able to directly access it, but can't.

Hence, you should be able to do:

someObject = nil;

instead of

self.someObject = nil;

In the meantime, using the accessor directly is the only way to do it without providing an explicit ivar.

Update: This bug has been fixed; you can now do someObject = nil just fine.

BJ Homer
  • 47,750
  • 10
  • 111
  • 128
  • 3
    Interesting. After playing around a bit in Xcode, it appears you *can* access the synthesized ivar directly. Neat. – Dave DeLong Aug 17 '09 at 05:14
  • 1
    (A long time later) it was just pointed out to me that the documentation says that in the case of synthesized ivars, you should use the setter in the `-dealloc` method. Sorry, but this means I have to award the accepted answer to Rob instead. http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html – Dave DeLong Oct 11 '10 at 00:35
  • No, it doesn't say that you _should_ use the accessor, it says that because you can't access ivars directly, you have no other choice but to do so. This used to be the case, but in more recent versions of the Dev Tools, you can access the ivar directly. – BJ Homer Oct 11 '10 at 17:28