3

I have read a lot of topics about getters and setters. I know what they are and why are useful. Different source claim's different ways to release the ivars. Here begins my confusion

@interface CoolClass : NSObject
{
  NSString *name;
}

@property (nonatomic, copy) NSString *name;
@end

@implementation CoolClass
@synthesize name = _name;

-(id)init
{
  if(super = [self super])
  {
     self.name = @"Jo";
  }
  return self;
}

-(void)dealloc
{
  [self.name release], self.name = nil;
}
@end

Is that the correct way to release/free ivars ?

Profo
  • 153
  • 2
  • 2
  • 11
  • 1
    possible duplicate of [Why shouldn't I use the getter to release a property in objective-c?](http://stackoverflow.com/questions/7262268/why-shouldnt-i-use-the-getter-to-release-a-property-in-objective-c) – jrturton Feb 06 '12 at 09:31
  • @Profo Post answer: I missed that you'd written `super = [self super]` -- it should be `self = [super init]` – justin Feb 06 '12 at 09:59
  • if((super = [super init])) should also be written with double (( )) to avoid a compiler warning. – Nick Lockwood Feb 06 '12 at 11:02

3 Answers3

2

You'll want to use accessors most of the time, but not in partially constructed states because they can have negative side-effects. Here's how it's done:

- (id)init
{
  if((self = [super init])) {
     // self.name = @"Jo"; << don't use accessors in initializer

     _name = [@"Jo" copy]; << good
  }
  return self;
}
// added for another variation:
- (id)initWithName:(NSString *)inName
{
  if((self = [super init])) {
     _name = [inName copy];
  }
  return self;
}

- (void)dealloc
{
    // don't use accessors in dealloc
    // don't release the result of a getter (release the result of the retained or copied result)
    // [self.name release], self.name = nil;  

    // instead:
    [_name release], _name = nil;
    [super dealloc]; << compiler should have warned you about this one
}

Note: In the case of init, the string literal is an immortal and it won't matter if you copy it because the copy just returns itself. My preference is to just 'copy' the immortal for clarity, although it's unnecessary.

Community
  • 1
  • 1
justin
  • 104,054
  • 14
  • 179
  • 226
  • why you shouldn't use accessors in the initializer's ? – CarlJ Feb 06 '12 at 09:54
  • What do you mean by "immortal" ? – Profo Feb 06 '12 at 10:01
  • 1
    @meccan here's an example i wrote up which demonstrates some of the things that can go wrong: http://stackoverflow.com/questions/5932677/initializing-a-property-dot-notation/5932733#5932733 – justin Feb 06 '12 at 10:04
  • @Profo `NSString` literals (e.g. `@"Hi there!"`) live for the life of your program and are never dealloc'ed. – justin Feb 06 '12 at 10:04
  • 1
    @meccan I use them quite a bit myself too, but I've been writing objc for 9 years now so i've made enough mistakes that it doesn't affect me terribly often. you may like this post: http://stackoverflow.com/questions/9086736/why-would-you-use-an-ivar/9086811#9086811 – justin Feb 06 '12 at 10:29
  • @profo, it's still good practice to retain/copy/release it as normal because if it changes from a literal to something else you wouldn't want to introduce a leak. – Nick Lockwood Feb 06 '12 at 10:32
  • 1
    @Justin i just checked som objC / Cocoa Developer Book from Apress and the authors often user the accessor instead the ivar in the initializer... crazy =) – CarlJ Feb 06 '12 at 10:45
  • @meccan I've worked with people who have *insisted* on it. not just that but also things like "just call `viewDidUnload` in `dealloc` to zero the IBOutlets. if we notice an issue in development…". – justin Feb 06 '12 at 10:56
  • @Justin last Question: What is the best way to set the properties in an initializer if you extend from e.g. UILabel, because you can't access the iVars in the initializer... – CarlJ Feb 06 '12 at 11:02
  • 1
    @meccan for self's properties, i just use one of the designated initializers, set up my ivars in init, and leave the rest to the client. in special cases where I really really want this functionality, I'll use a convenience constructor to accomplish this. – justin Feb 06 '12 at 11:10
  • @Justin you mean a Class Method where you return a autoreleased object? e.g. -(MyLabel*)labelWithText:(NSString *)text { MyLabel *l = [[MyLabel alloc ]init]; l.text = text; return [l autorelease];} ?? – CarlJ Feb 06 '12 at 11:32
  • 1
    @meccan yup. either class method: `+ (MyLabel *)labelWithText:` or `+ (MyLabel *)newLabelWithText:` – justin Feb 06 '12 at 11:37
  • and why is apple using the behavior in the docus? Is this the wrong way? https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW35 – CarlJ Feb 07 '12 at 08:57
  • @meccan i can't speak for apple. however, the approach in the sample could be a source of bugs -- the implementations can change at any time, and we've no guarantee it would be without side effects. it also depends on what your implementations override and implement. when programs reach a certain amount of complexity, it is best (IMO) to observe the boundaries of defined order and behavior. a lot of people think i'm over-cautious -- 98% success rate of a program is just not good enough (IMO). programs that make frequent exceptions require a lot of maintenance (YMMV). – justin Feb 07 '12 at 09:13
1

Here is what I would advise:

@interface CoolClass : NSObject
@property (nonatomic, copy) NSString *name;
@end

@implementation CoolClass
@synthesize name = _name;

-(id)init
{
  if(super = [self super])
  {
     self.name = @"Jo";
  }
  return self;
}

-(void)dealloc
{
  [_name release];
  [super dealloc];
}

@end

Notes:

  1. There is no need to explicitly declare ivars inside { ... } in your header. They will be created automatically when you synthesise your property. Explicit ivars are a legacy concept that are no longer needed since about iOS 3.

  2. You should not use self.name in the dealloc as this calls the getter method, which may do additional work beyond merely fetching the ivar. Normally it's good practice to use the getter method, but in the dealloc you should release the ivar directly

  3. It is good practice to set ivars to nil after releasing them, but again in the dealloc this in not necessary because no code is ever executed after dealloc, so the pointer won't be referenced again.

  4. Normally (outside of the dealloc), if you wish to release an ivar you should set it to nil using the setter like this: self.name = nil; that will automatically release it and set it to nil. This is equivalent to [_name release], _name = nil;

Nick Lockwood
  • 40,865
  • 11
  • 112
  • 103
  • @Justin notice above that self.name = @"Jo" is incorect, can you confirm that ? – Profo Feb 06 '12 at 10:04
  • @Nick Regarding #3: dangling references in `dealloc` bite. it's uncommon, but it happens. – justin Feb 06 '12 at 10:26
  • Only if the dealloc of a subclass violates the "don't do any work" rule. For example if you call a setter/getter method that references other ivars. If the only thing you put in dealloc is release statements and deregister from notification and KVO observers (like you're supposed to), you'll be fine. – Nick Lockwood Feb 06 '12 at 10:31
  • @NickLockwood that's not how all real object graphs are constructed - there are still corner cases such as weak references. – justin Feb 06 '12 at 10:47
  • Fair enough. It's not a problem I've run into but I can see how it could happen theoretically. Along the same lines, you shouldn't set ivars directly in init, because init may call another inherited init that also sets the same ivar. – Nick Lockwood Feb 06 '12 at 10:50
  • @NickLockwood that's not an issue here - I always use `@private` ivars and never call parallel initializers. – justin Feb 06 '12 at 10:58
1
@interface CoolClass : NSObject
{
  NSString *name;
}

You declared here an instance variable 'name'; Nowadays there is no need to declare ivars in the header file. Just use properties and make the compiler to synthesize ivar for you.

@property (nonatomic, copy) NSString *name;

Here we have a property declaration that specifies that a copy of the object should be used for assignment and that a previous value is sent a release message.

In implementation you want to synthesize your property:

@synthesize name = _name;

This code tells the compiler to generate a getter and setter for property called 'name' and use instance variable called '_name' to store value. So you have now two ivars - 'name' and '_name'.

That how init method should like like:

-(id)init
{
  if(self = [super init])
  {
     name = @"This is ivar declared between {}";
     _name = @"synthesized ivar";

  }
  return self;
}

And the dealloc:

-(void)dealloc
{
  [name release];
  [_name release];
  [super dealloc];
}
nagan
  • 119
  • 3