Do you have to release IBOulets in dealloc? - iphone

Do you have to release IBOulets in dealloc? I'm not sure on this one because I didn't do the alloc and typically you only release for something you called alloc on. Anyone know?

Your IBOutlets are probably #properties. If they are, and you have retain as an attribute, then you do need to release in -dealloc
In other words:
#interface MyViewController : UIViewController {
IBOutlet UITableView *myTable;
}
#property (nonatomic, retain) IBOutlet UITableView *myTable;
#end
You will have to [myTable release]; in your dealloc.
If you make a new Navigation Based App in Xcode, and look in the appdelegate.h:
#interface Untitled1AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UINavigationController *navigationController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
#end
and the dealloc for appdelegate.m:
- (void)dealloc {
[navigationController release];
[window release];
[super dealloc];
}
The key thing to see here are lines like these:
#property (nonatomic, retain) IBOutlet UIWindow *window;
If there is a retain in there, that means that the property is being "owned" by your code and you have to release it.
Of course, there are other ways, such as not declaring the IBOutlets as properties, or declaring them as properties without retaining. I find that in most cases I prefer to have them be retained properties, which I then have to explicitly release. An example of this is when you flip from one view controller to another. As one view controller is dismissed, its views are removed and they are released. Any IBOutlet UILabels on that view would get released too if I don't have them retained. That means that when I flip back to the old view, I have to go through and reset my labels and controls to their last values, when I could have easily kept them saved if I just retain the IBOutlet.

If you just use IBOutlet in your interface, you DO NOT need to release them. Reason being is that unless you explicitly retain them in your code, they are merely being set. They stick around because the view is there. Obviously, if you also use properties and retain them, you need to release on dealloc.

It's not about IBOutlet, it's about your declaration.
If you use a new project wizard in Xcode, you probably get some code like this in your header file.
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
You can see, there's retain keyword in the header file. Following the memory management guideline, you MUST release everything your retain (by calling alloc, copy, retain, etc.). And you have retain in your code then you must release it.
Additionally, the wizard already add some release code for you.
- (void)dealloc {
[tabBarController release];
[window release];
[super dealloc];
}

As you said, you should release everything that you allocated yourself (with alloc or copy). It works the other way: you should not release any Cocoa objects you did not allocate yourself (some CoreFoundation functions allocate, and you're responsible for releasing them, but it's not the case here).
If you didn't allocate your IBOutlet, then you don't have to release it, unless of course, for some reason, you retained it somewhere.

To answer the side question by Joe D'Andrea. You can use self.label = nil;. Because it is calling setLabel, which is auto generated:
- (void)setLabel:(UILabel *)input
{
[label autorelease];
label = [input retain];
}
As you can see the current label will be released then nil is assigned to label.
But make sure you don't write it as label = nil. That will not work.
Because you need to call the auto generated label accessor method.

Here's what I have been doing with regard to IBOutlet objects (in conjunction with a NIB file):
#interface MyViewController : UIViewController {
UILabel *label;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#end
#implementation MyViewController
#synthesize label;
- (void)setView:(UIView *)aView {
if (!aView) {
// view is being set to nil
// set outlets to nil to get the benefit of the didReceiveMemoryWarning!
self.label = nil;
}
// Invoke super's implementation last
[super setView:aView];
}
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.label = nil;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)dealloc {
[label release];
[super dealloc];
}
Side question: Does it make more sense to use self.label = nil in dealloc, or must release be explicitly called (for instance, to keep the static analyzer happy)?
I suppose, at that point, we're on our way out anyway, so there's no need to set our IBOutlet objects to nil.

If this is your BlahViewController.h:
// BlahViewController.h
#import <UIKit/UIKit.h>
#interface BlahViewController
{
IBOutlet UINavigationBar *navigationBar;
}
#property (nonatomic, retain) IBOutlet UINavigationBar *navigationBar;
#end
Then this would be your dealloc in BlahViewController.m:
- (void)dealloc
{
[navigationBar release];
[super dealloc];
}
However, if this is your BlahViewController.h:
// BlahViewController.h
#import <UIKit/UIKit.h>
#interface BlahViewController
{
IBOutlet UINavigationBar *navigationBar;
}
#end
Then this would be your dealloc in BlahViewController.m:
- (void)dealloc
{
[super dealloc];
}
And finally, if this is your BlahViewController.h:
// BlahViewController.h
#import <UIKit/UIKit.h>
#interface BlahViewController
{
IBOutlet UINavigationBar *navigationBar;
IBOutlet MKMapView *map;
}
#property (nonatomic, retain) IBOutlet UINavigationBar *navigationBar;
#end
Then this would be your dealloc in BlahViewController.m:
- (void)dealloc
{
[navigationBar release];
[super dealloc];
}
In short, if you declare it as a property, with retain, then you need to release it.

Related

Memory leak vs Zombie - iPhone

My iPhone app is either crashing due to to a zombie, or leaking memory.. I've narrowed it down to 3 lines of code and can reliably get one of the two things to happen by commenting/uncommenting the code. The bugs occur when navigation between a list of results (tableView) and a details page containing a map and a few labels, memory leak happens the first time I navigation from the map back to the list of results, the zombie occurs after maybe 5/6 times navigating to different results and back.
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#define METERS_PER_MILE 1609.344
#interface ResDetailsPageVC : UIViewController <MKMapViewDelegate, UIAlertViewDelegate> {
UISegmentedControl *mapTypeSwitcher;
MKMapView *mapView;
UILabel *nameLabel;
UIButton *addressLabel;
UILabel *telephoneLabel;
NSString *address;
}
#property (nonatomic, retain) IBOutlet UISegmentedControl *mapTypeSwitcher;
#property (nonatomic, retain) IBOutlet MKMapView *mapView;
#property (nonatomic, retain) IBOutlet UILabel *nameLabel;
#property (nonatomic, retain) IBOutlet UIButton *addressLabel;
#property (nonatomic, retain) IBOutlet UILabel *telephoneLabel;
- (IBAction)segmentedControlIndexChanged;
- (IBAction)callButtonClick;
- (IBAction)addressClick;
- (void) callNumber;
#end
#synthesize mapView;
#synthesize mapTypeSwitcher;
#synthesize nameLabel, addressLabel, telephoneLabel;
- (void)dealloc {
// if these lines are commented out - memory leak
// if not - zombie?!
/*self.telephoneLabel = nil;
self.addressLabel = nil;
self.nameLabel = nil;*/
self.mapView = nil;
self.mapTypeSwitcher = nil;
[super dealloc];
}
Somewhere some other piece of code is using the same object whose address is stored in one of those three properties, but that other piece of code has not properly retained the object.
I recommend this to you:
- (void)dealloc {
[telephoneLabel release]; telephoneLabel = nil;
[addressLabel release]; addressLabel = nil;
....
[super dealloc];
}

Change text in UILabel with NSMutablearray data

I'm trying to change the text of a UILabel with text from an array upon a button click, but it doesn't do anything.
#interface Test01AppDelegate : NSObject <UIApplicationDelegate> {
UILabel *helloLabel;
UIButton *hellobutton;
NSMutableArray *madWords;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UIButton *hellowButton;
#property (nonatomic, retain) IBOutlet UILabel *hellowLabel;
#property (nonatomic, retain) NSMutableArray *madWords;
- (void) madArrays;
- (IBAction)helloYall;
#end
and
#import "Test01AppDelegate.h"
#implementation Test01AppDelegate
#synthesize window = _window;
#synthesize hellowButton;
#synthesize hellowLabel;
#synthesize madWords;
- (void) madArrays {
[madWords addObject:#"Part A"];
[madWords addObject:#"Part B"];
[madWords addObject:#"Part C"];
[madWords addObject:#"Part D"];
}
- (IBAction)helloYall {
[self madArrays];
self.hellowLabel.text = [madWords objectAtIndex:0];
}
I can set the helloLabel text with
#"some text here";
and it works fine. Also, I tried copying the "madArrays" method into the "helloYall" method and it still didn't work. As I said, I can manually set the text and it works, but I'd like to pull the info from an array. Eventually, I'd like to loop through the array to grab the text on each button press, but one step at a time. Thanks.
You never create the madWords array. You need to add:
self.madWords = [NSMutableArray array];
at the top of:
- (void) madArrays {
would probably be a good place. Other possibly good places would be i the class init method or the view controller viewWillAppear method.
// Or you can try this in your init Method:
//first allocate the ivar
- (void)myInitMethod {
madArrays = [[NSMutableArray]alloc]init];
}
//then you can do anything directly to the ivar or throughout de setter
- (void)doAnythingWithiVar {
// do your stuff
}
//when you are done you can dealloc your ivar
- (void)dealloc {
[madArrays release];
[super dealloc];
}
It looks like madArrays is still nil when you come to populate it. At some point you need something like [self setMadArrays:[[NSMutableArray alloc] init]];. Also, don't forget to release madArrays in the dealloc before calling super as you'll have a memory leak.

Unused IBOutlets leaking

So, I'm loading by XIB file and it contains a set of UIBarButtonItems. Some of the items are used when the viewDidLoad: is called.
#interface MyViewController : UIViewController {
IBOutlet UIBarButtonItem *addButton;
IBOutlet UIBarButtonItem *editButton;
IBOutlet UIBarButtonItem *doneButton;
}
// NB: There are no properties retaining anything.
#end
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *initialToolbarItems =
[[NSArray alloc] initWithObjects: addButton, editButton, nil];
self.toolbarItems = initialToolbarItems;
[initialToolbarItems release];
}
- (void)dealloc {
[super dealloc];
// Nothing else to do here since we are not retaining anything.
// … or are we? <insert dramatic music here>
}
#end
If I push the above the above ViewController onto a UINavigationController everything seems fine, all the IBOutlets are assigned and behave like expected.
The instant i pop the ViewController from the navigation stack Instruments' Leaks tells me that I am leaking a UIBarButtonItem. Woe is me!
If I change dealloc: to
- (void)dealloc {
[doneButton release];
[super dealloc];
}
no leaks occur. The same goes if I use doneButton in viewDidLoad:
NSArray *initialToolbarItems =
[[NSArray alloc] initWithObjects: addButton, editButton, doneButton, nil];
My question: Why is my IBOutlet leaking when I don't use it. I don't retain it at any point. The the NIB loader should own the object, right?
Only thing I can think of:
The nib loader treats IBOutlets as strong references. All outlets are retained by default unless you specifically indicate assign. So you still need to release them in dealloc and viewDidUnload.
You can also use a assigned property to make it a weak reference:
#property (nonatomic, assign) IBOutlet UIBarButtonItem *doneButton;
Some reading: http://weblog.bignerdranch.com/?p=95
If you have #property with (retain) declared for the your IBOOutlets they will be retained and must be released
The array retains them

App Crashes Loading View Controllers

I have the following code in my AppDelegate.h file:
#class mainViewController;
#class AboutViewController;
#interface iSearchAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
mainViewController *viewController;
AboutViewController *aboutController;
UINavigationController *nav;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet mainViewController *viewController;
#property (nonatomic, retain) IBOutlet AboutViewController *aboutController;
#property (nonatomic, retain) IBOutlet UINavigationController *nav;
[...IBActions declared here...]
#end
Then, in my .m file:
#implementation iSearchAppDelegate
#synthesize window;
#synthesize viewController, aboutController, settingsData, nav, engines;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[window addSubview:nav.view];
[window addSubview:aboutController.view];
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
-(IBAction)switchToHome{
[window bringSubviewToFront:viewController.view];
}
-(IBAction)switchToSettings{
[window bringSubviewToFront:nav.view];
}
-(IBAction)switchToAbout{
[window bringSubviewToFront:aboutController.view];
}
- (void)dealloc {
[viewController release];
[aboutController release];
[nav release];
[window release];
[super dealloc];
}
#end
Somehow, when I run the app, the main view presents itself fine... however, when I try to execute the actions to switch views, the app crashes with an EXC_BAD_ACCESS.
So, I think it has something to do with memory management, but I'm not quite sure.
Thanks for any help in advance.
Link to screenshots of code is here: link...
SOLVED: I fixed the issue by taking out the IBActions and making them into regular methods... apparently, XCode doesn't like it when you put IBActions in an AppDelegate.
... message sent to deallocated instance ...
If it is memory management, my first step would be to enable NSZombie and discover what was being messaged after being dealloc'ed. Two obvious things I can think of:
Uninitialised property/variable.
De-allocated (non-retained) property
Have your controls in interface builder been connected to the IBActions?
Somewhere in your code, you are invoking [iSearchAppDelegate performSelector:withObject:withObject:]. You haven't shown that code here but that's likely where the problem is.

How to pass a variable from one view controller to another?

I have three view controllers, one root controller, one login view controller and one customers view controller. I want to pass the entered username and password in login view controller to the customers view controller. My files and code is displayed below, could you please guide me, how can access to variables set in the login view controller? Or how can I pass variables to customers view controller?
I have these class files:
/classes/MySoftwareAppDelegate.h
/classes/MySoftwareAppDelegate.m
/classes/ViewController.h
/classes/ViewController.m
/classes/LoginController.h
/classes/LoginController.m
/classes/CustomersController.h
/classes/CustomersController.m
I have these views:
/resources/MainWindow.xib
/resources/Login.xib
/resources/Customers.xib
In the AppDelegate, I have successfully inserted the sub view "Login" and it's displayed whenever the app starts.
In the login view, I enter my username and password and then click the "Login" button. When this button is clicked, an IBAction is triggered. In this IBAction, I want to change the current subview with the Customers.
Here's the code I have used:
MySoftwareAppDelegate.h
#import <UIKit/UIKit.h>
#class ViewController;
#interface MySoftwareAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
ViewController *viewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet ViewController *viewController;
#end
MySoftwareAppDelegate.m
#import "MySoftwareAppDelegate.h"
#import "ViewController.h"
#implementation MySoftwareAppDelegate
#synthesize window;
#synthesize viewController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after application launch
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
#end
ViewController.h
#import <UIKit/UIKit.h>
#class LoginController;
#interface ViewController : UIViewController {
LoginController *loginController;
}
#property (nonatomic, retain) LoginController *loginController;
#end
ViewController.m
#import "ViewController.h"
#import "LoginController.h"
#implementation ViewController
#synthesize loginController;
- (void)viewDidLoad {
LoginController *tmpViewController = [[LoginController alloc] initWithNibName:#"Login" bundle:nil];
self.loginController = tmpViewController;
[self.view insertSubview:loginController.view atIndex:0];
[tmpViewController release];
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
if (self.loginController.view.superview == nil) {
self.loginController = nil;
}
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[loginController release];
[super dealloc];
}
#end
LoginController.h
#import <UIKit/UIKit.h>
#class CustomersController;
#interface LoginController : UIViewController {
UIButton *loginButton;
UITextField *usernameTextField;
UITextField *passwordTextField;
NSMutableString *available_credits;
NSString *current_xml_element;
CustomersController *customersController;
}
#property (nonatomic, retain) IBOutlet UIButton *loginButton;
#property (nonatomic, retain) IBOutlet UITextField *usernameTextField;
#property (nonatomic, retain) IBOutlet UITextField *passwordTextField;
#property (nonatomic, retain) NSMutableString *available_credits;
#property (nonatomic, retain) NSString *current_xml_element;
#property (nonatomic, retain) CustomersController *customersController;
-(IBAction)textFieldDoneEditing:(id)sender;
-(IBAction)backgroundTap:(id)sender;
-(IBAction)loginToAccount:(id)sender;
#end
LoginController.m
#import "LoginController.h"
#import "CustomersController.h"
#implementation LoginController
#synthesize loginButton;
#synthesize usernameTextField;
#synthesize passwordTextField;
#synthesize customersController;
- (void)viewDidLoad {
UIImage *buttonImageNormal = [UIImage imageNamed:#"whiteButton.png"];
UIImage *stretchableButtonImageNormal = [buttonImageNormal stretchableImageWithLeftCapWidth:12 topCapHeight:0];
UIImage *buttonImagePressed = [UIImage imageNamed:#"blueButton.png"];
UIImage *stretchableButtonImagePressed = [buttonImagePressed stretchableImageWithLeftCapWidth:12 topCapHeight:0];
[loginButton setBackgroundImage:stretchableButtonImageNormal forState:UIControlStateNormal];
[loginButton setBackgroundImage:stretchableButtonImagePressed forState:UIControlStateHighlighted];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[usernameTextField release];
[passwordTextField release];
[super dealloc];
}
-(IBAction)textFieldDoneEditing:(id)sender {
[sender resignFirstResponder];
}
-(IBAction)backgroundTap:(id)sender {
[usernameTextField resignFirstResponder];
[passwordTextField resignFirstResponder];
}
-(IBAction)loginToAccount:(id)sender {
// bla bla bla... Login check process is done here
CustomersController *tmpViewController = [[CustomersController alloc] initWithNibName:#"Customers" bundle:nil];
self.customersController = tmpViewController;
[self presentModalViewController:tmpViewController animated:YES];
[self.view removeFromSuperview];
[tmpViewController release];
}
#end
As you can see above, in LoginController.m's loginToAccount method, I am checking the login info and then setting the new view controller for the "customers" sub-view.
Then I am removing the current "Login" subview from the super view but don't know how to add the new "Customers" sub view.
In MainWindow.xib, I have one view controller which is linked to ViewController class and it's the root contoller.
Any help is appreciated. Because I am new to Objective-C and iPhone programming, please do your best to explain considering a novice programmer :)
Thanks again.
Okay, let me answer my question. I just found the answer on StackOverFlow.com
In the view controller which is going to load the next view controller, just add these lines:
NextController *tmpViewController = [[NextController alloc] initWithNibName:#"NextView" bundle:nil];
tmpViewController.enteredUsername = usernameTextField.text;
tmpViewController.enteredPassword = passwordTextField.text;
I'd say that better way is to have separate class for storing globally needed data (and that would be compliant with MVC model).
For example you can store you login information in your MySoftwareAppDelegate, which is easily accessible with [[UIApplication sharedApplication] delegate] call from any part of your application.
It all depends on how serious the data you want to pass it. For a quick variable (maybe a settings change in a modal view controller) TamTam's solution makes the most sense. You alloc/init'ed it, you got the variable, why not access it properties? That same (modally presented) view controller might pass variables back via a delegate pattern.
If you're data needs to be system wide, you can use the singleton pattern. Using "[[UIApplication sharedApplication] delegate]" gets the application delegation (which is a singleton), and many people stuff their variables there for convenience. However, your app delegate wasn't designed for this, and so it's considered bad form. Create your own singleton if your apple isn't a quickie.
If you use a persistent data store like sql, plists or coredata, you can put your system wide data there.