7

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];   

}
PengOne
  • 48,188
  • 17
  • 130
  • 149
WheresWardy
  • 375
  • 3
  • 22
  • 1
    It may do better to break up your questions and post them separately since they are independent of one another. This will also help you find similar questions and possibly answers for them individually. – PengOne May 24 '11 at 16:39
  • I thought that, but they're all kind of related around the same question that's not really definitely answered anywhere (i.e., what's the correct way to refer to instance variables, IBOutlet or not?) – WheresWardy May 24 '11 at 17:20

3 Answers3

3

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.

kball
  • 4,923
  • 3
  • 29
  • 31
  • 2) So are you saying that you don't need to alloc/init when you use dot notation? You just give it a convenience version of the object? (e.g. @"String", [NSDictionary dictionaryWith...] etc.) - or that you release right after the alloc/init, which seems weird? 3) By fussy, I mean given that there's no getters/setters for scalars, do you need to adhere to dot notation? – WheresWardy May 24 '11 at 20:01
  • Editing answer above to address your comment – kball May 24 '11 at 20:07
  • Looking over some Apple sample projects they do set other instance variables to nil in viewDidUnload, presumably this is because they create them in viewDidLoad, and the view may be removed and recreated so you have to nil-value the instance variable in case this happens? – WheresWardy May 25 '11 at 16:35
3

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.

Marc Charbonneau
  • 40,399
  • 3
  • 75
  • 82
  • Thanks for this, explains it well. So what's the point of the synthesized accessor methods if you're just going to alloc/init and release yourself every time you want to use the instance variable? Presumably it's so that you can alloc/init in one method and release in another method you want to use the value of the variable in, but I'm fairly sure that Clang's analyzer would complain about that? Or is there a better way to declare values for those instance variables than alloc/init every time? – WheresWardy May 24 '11 at 21:08
  • I'm not sure what you mean by "every time you want to use the instance variable." The generated getter and setter methods are fine to use, except during object creation and deletion. Otherwise you access the instance variables directly. – Marc Charbonneau May 25 '11 at 00:42
  • By when you want to use it, I mean it sounds like you have to create it and release it every time you use it somewhere - http://pastie.org/1970738. So standard practice is not to use the setter during creation? Is there a decent example of all this together somewhere? It seems like every thing I've read uses a real mix of what you're not supposed to do, like referring to local variables etc. – WheresWardy May 25 '11 at 11:21
1
  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 !

Jonathan.
  • 53,997
  • 54
  • 186
  • 290
Dani Pralea
  • 4,545
  • 2
  • 31
  • 49
  • 2. I was under the impression that they were the same thing, but not in the instance of setters and getters? Referring to myVariable instead of self.myVariable still works. – WheresWardy May 24 '11 at 17:14
  • If you have them named identically, the local variable will hide the instance variable. Try and see what happens (what values they have etc. ) – Dani Pralea May 24 '11 at 17:21
  • In dealloc you should access the instance variables directly, using the accessors may call code that shouldn't be called when an object is being deallocated. – Marc Charbonneau May 24 '11 at 19:18
  • There are a lot of circumstances in Apple's sample code where they sometimes refer to `variable` instead of `self.variable`, is this just laziness? For example, the label instance variable in the `-(void)showPicker:(UIView *)picker` method here: http://developer.apple.com/library/ios/samplecode/UICatalog/Listings/PickerViewController_m.html – WheresWardy May 25 '11 at 16:40