Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IBOutlets, instance variables and properties: Best Practices

I've done all sorts of research today on best practices with regards to declaring IBOutlets and instance variables, managing them, using the correct accessors and properly releasing them. I'm pretty much there, but I've got some niche questions that I hope somebody will be able to advise the best practice on. I'll format them as code and comment the questions so as to make it easier to understand. I've excluded some obvious parts that I didn't think were relevant and can be safely assumed to work (like pre-processor stuff, @end, required implementation methods etc).

MyViewController.h

@class OtherViewController;

@interface MyViewController : UIViewController {

     NSString *_myString;
     BOOL _myBOOL;

}

// The first two properties aren't declared in the interface
// above as per best practices when compiling with LLVM 2.0

@property (nonatomic, retain) OtherViewController *otherViewController;
@property (nonatomic, retain) UIButton *myButton;
@property (nonatomic, copy) NSString *myString;
@property (readwrite) BOOL myBOOL;

MyViewController.m

@implementation MyViewController

// Synthesizing IBOutlets on iOS will cause them to be
// retained when they are created by the nib

@synthesize otherViewController;
@synthesize myButton;

// Assign instance variables so as to force compiler
// warnings when not using self.variable

@synthesize myString = _myString;
@synthesize myBOOL = _myBOOL;

- (void)viewDidLoad {

     // QUESTIONS:

     // 1. Ignoring convenience methods, can you still alloc and init in dot notation
     //    even when it's being properly synthesized?

     self.myString = [[NSString alloc] initWithString:@"myString"];
     self.myString = existingNSStringObject;

     // 2. Should you always call methods for IBOutlets and instance variables using dot notation?
     //    Is there any difference seeing as these aren't directly invoking setters/getters?

     [self.myButton setText:self.myString];
     [myButton setText:self.myString];

     [self.otherViewController.view addSubview:mySubview];
     [otherViewController.view addSubview:mySubview];

     [self.myButton setAlpha:0.1f];
     [myButton setAlpha:0.1f];
     self.myButton.alpha = 0.1f;
     myButton.alpha = 0.1f;

     // 3. How fussy are scalar variables in terms of getters and setters,
     //    given that there is a @synthesize declaration for them?

     self.myBOOL = YES;
     myBOOL = NO;

     if(self.myBOOL) { ... }
     if(myBOOL) { ... }

     // 4. On instantiation of new view controllers from NIBs, should you use
     //    dot notation? (I haven't been doing this previously).

     otherViewController = [[OtherViewController alloc] initWithNibName:@"OtherView" bundle:nil];
     self.otherViewController = [[OtherViewController alloc] ... ]

}

- (void)viewDidUnload {

     // 5. Best practice states that you nil-value retained IBOutlets in viewDidUnload
     //    Should you also nil-value the other instance variables in here?

     self.otherViewController = nil;
     self.myButton = nil;

     self.myString = nil;

}

- (void)dealloc {

     [otherViewController release];
     [myButton release];
     [_myString release];   

}
like image 449
WheresWardy Avatar asked May 24 '11 16:05

WheresWardy


3 Answers

I always declare and explicitly set a property's underlying instance variable. It's a little more work up front, but in my mind it's worth it to explicitly differentiate variables and properties and see at a glance what instance variables a class has. I also prefix instance variable names, so the compiler complains if I accidentally type property instead of object.property.

  1. Calling alloc / init creates an object with a retain count of 1. Your synthesized property will also retain the object, causing a memory leak when it's released (unless you release your property right after, but that's bad form). Better to alloc / and release the object on a separate line.

  2. Dot notation is effectively the same as calling [self setObject:obj]. Not using dot notation accesses the underlying instance variable directly. In init and dealloc, always access the instance variable directly as the accessor methods can include extra operations (such as key value observing notifications) that are not valid when the object is being created or destroyed. All other times use the synthesized accessor methods. Even if you're not doing anything special now, you might later override these methods later to change what happens when the variable is set.

  3. Scalars work the same way, only you don't have to worry so much about memory.

  4. One accesses the synthesized accessor methods, the other accesses the instance variable directly. See questions one and two again, and be careful about memory leaks!

  5. The view controller may be pushed onto the screen again, in which case your viewDidLoad method will be called a second time. If you're setting initial values in viewDidLoad, go ahead and set your properties to nil here. This makes sense for properties that use a lot of memory and aren't going to affect the state of the view. On the other hand if you want the property to persist until you're sure it's no longer needed, create it in your init method and don't release it until dealloc.

like image 196
Marc Charbonneau Avatar answered Oct 31 '22 10:10

Marc Charbonneau


1) You've slightly misunderstood @synthesize. @synthesize does nothing with the object. It only tells the compiler to generate the getter and setter methods according to the options used in your @property declaration

// Synthesizing IBOutlets on iOS will cause them to be

// retained when they are created by the nib

The outlets aren't retained (outlets are just notices to interface builder and don't affect the code), the objects are retained when the setter generated by @synthesize is used. When the nib is loaded, the loading system calls your generated setter.

2) Deciding whether to use accessors in objective C is no different from deciding to use accessors in any other object oriented language. It is a choice of style, need and robustness. That the accessor is serving as an IBOutlet makes no difference.

But in objective C I would suggest you should NOT use accessors in two places: dealloc and within the var's accessor method itself.

And if you ARE using the accessors in init then you need to be careful about your retain counts.

self.myString = [[NSString alloc] initWithString:@"myString"];

This line leaks memory. Using your copy accessor retains the object, so you should release it here after creating it.

3) Not sure what you mean by fussy. Possibly see answer to 2)

4) See 2) and be careful about memory management. If you call alloc/init you are now responsible for releasing the object - this is entirely independent of the retains/releases used by accessors and dealloc.

5) No, you should not nil other instance variables in viewDidUnload. Your controller is expected to maintain its state even if the view goes away. viewDidUnload is only for cleaning up potentially memory-heavy view objects when the controller's view is not currently on screen.

Consider a navigation controller. View controller 1 is on the stack and then view controller 2 is pushed and is now visible. If memory conditions get low, the system could attempt to unload view controller 1's view and will then call viewDidUnload.

Then popping view controller 2 will not create the view controller 1 object again, but it WILL load view controller 1's view and call viewDidLoad.

Re comments

2) That's exactly right - you can use a convenience constructor or release immediately after your alloc/init and assignment, or release before the block exits, or autorelease. Which you choose is mostly a matter of style (though some would argue against autorelease - but not me!)

3) There are accessors for scalars - you have created some in your code

@property (readwrite) BOOL myBOOL;

This creates methods myBOOL and setMyBOOL on your class.

Remember that there is nothing special about dot notation. It is only a convenience and when the code is compiled myObject.property is exactly equivalent to [myObject property] and myObject.property = x is exactly equivalent to [myObject setProperty:x]. Using dot notation is purely a style choice.

like image 3
kball Avatar answered Oct 31 '22 11:10

kball


  1. Dot notation and brackets notation are pretty much the same.
  2. By self.myVariable you are accessing the getter of the property of the instance variable myVariable and by myVariable you are accessing the local variable. They're not the same thing.
  3. You can customize the setters and the getters by overriding the methods and specific some certain conditions for them.
  4. See first answer ( brackets are preferred - better understanding of the code )
  5. Better make a separate method.

Like:

- (void) releaseOutlets {
 self.firstOutlet = nil;
 self.mySecondOutlet = nil;
 ……………………
 self.myLastOutlet = nil;
}

and then call this method both in viewDidUnload and in dealloc methods.

Hope it helps !

like image 1
Danut Pralea Avatar answered Oct 31 '22 12:10

Danut Pralea