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
Related
I am having a Navigation based app with a few buttons on the first View (not using ARC). By touching one button optionPressed gets triggered to push to another View.
When I analyse the code for leaks. I get the following warning. "Potential leak of an object" [self.displayViewController setCurrentPhoto:sender.currentTitle];
How should I release the self.displayViewController and where if that's the cause.
.h
#import <UIKit/UIKit.h>
#import "DisplayViewController.h"
#class DisplayViewController;
#interface Pocket_DjangoViewController : UIViewController
- (IBAction)optionPressed:(UIButton *)sender;
#property (retain, nonatomic) DisplayViewController *displayViewController;
#end
.m
- (IBAction)optionPressed:(UIButton *)sender
{
if (!self.displayViewController) {
self.displayViewController = [[DisplayViewController alloc] initWithNibName:#"DisplayViewController" bundle:nil];
}
[self.displayViewController setCurrentPhoto:sender.currentTitle];
[self.navigationController pushViewController:self.displayViewController animated:YES];
//[self.displayViewController release];
//self.displayViewController = nil;
}
The leak stems for this line:
self.displayViewController = [[DisplayViewController alloc] initWithNibName:#"DisplayViewController" bundle:nil];
you should have:
self.displayViewController = [[[DisplayViewController alloc] initWithNibName:#"DisplayViewController" bundle:nil] autorelease];
In your actual code, you are creating an object:
[[DisplayViewController alloc] initWithNibName:#"DisplayViewController" bundle:nil];
which is already retained; then you assign it to a retain property:
#property (retain, nonatomic) DisplayViewController *displayViewController;
and this will create a retain unbalance, as the original alloc is never released and only the retain called by the property is eventually released.
I am passing an NSDictionary object from one view class to another as I transition from a table view to a normal view to show details:
Passing Controller:
[tweetController setTweet:tweet];
Receiving Controller.h:
#interface TweetViewController : UIViewController {
NSDictionary *tweet;
}
#property (nonatomic, retain) NSDictionary *tweet;
Receiving Controller.m:
#implementation TweetViewController
#synthesize tweet = _tweet;
I then try to use this information to set the properties of some fields in my view:
- (void)viewDidLoad
{
[super viewDidLoad];
tweetLabel.text = [_tweet objectForKey:#"text"];
}
The result is a blank label and if I inspect the value of _tweet at this stage it is nil.
I originally had a method which set the value of tweet which I called at the same location as I am now setting the value. If I inspected the value at this stage it was fine.
I presume that the automagic setter through #synthasize is working, but somewhere else the value is being lost.
Sorry this is my first objective C anything! Thanks for any help in advance.
You are using your "tweet" instance variable, whereas the "tweet" property is synthesized to the "_tweet" variable.
You are probably calling the setTweet method after viewDidLoad executes.
I usually pass this kind of thing into a custom init method.
Alternatively, you could do the set before pushing the detail VC onto the nav stack.
Are you sure that tweetLabel isn't nil?
I've made a few corrections & optimisations to your code. You don't need to declare ivars in the header file anymore, they are generated automatically by #synthesize
- (void)dealloc; is only needed if you're not using ARC.
//.h
#interface TweetViewController : UIViewController
#property (strong, nonatomic) NSDictionary *tweet;
#property (strong, nonatomic) IBOutlet UILabel *tweetLabel
#end
//.m
#implementation TweetViewController
#synthesize tweet = _tweet;
#synthesize tweetLabel = _tweetLabel;
- (void)viewDidLoad {
[super viewDidLoad];
self.tweetLabel.text = [self.tweet objectForKey:#"text"];
}
- (void)dealloc {
[_tweet release];
[_tweetLabel release];
[super dealloc];
}
#end
Note: strong is equivalent to retain
To expand on #Rayfleck's answer, since you are new to Objective-C, your custom init method could look like this:
In TweetViewController.h:
- (id)initWithTweet:(NSDictionary*)tweet;
In TweetViewController.m:
- (id)initWithTweet:(NSDictionary*)tweet
{
self = [super init];
if (self) {
_tweet = tweet;
}
return self;
}
and then in your passing controller you'd allocate and initialize like this:
TweetViewController *tvc = [[TweetViewController alloc] initWithTweet:myTweet];
I have an array for IBOutlet collection
.h
#interface UpisiRezultat : UIViewController {
NSArray *buttons;
}
#property (nonatomic, retain) IBOutletCollection(UIButton) NSArray *buttons;
.m
#synthesize buttons;
- (void)viewDidLoad
{
[self setValue:[UIFont fontWithName:#"NeverSayNever" size:22] forKeyPath:#"buttons.font"];
[super viewDidLoad];
}
- (void)viewDidUnload
{
buttons = nil;
}
- (void)dealloc
{
[buttons release]; --> Error
[super dealloc];
}
Why does my program crash when I have [buttons release]; in dealloc?
Without it, it doesn't crash...
updated(Dec1) code and Tested.
- (void)dealloc {
self.buttons = nil;
[super dealloc];
}
you should not release them.
http://www.bobmccune.com/2011/01/31/using-ios-4s-iboutletcollection/
If you have made a connection to your buttons with Interface Builder, it's your view that owns it and will release it.
Since buttons is an NSArray and it is explicitly retained, then it must be released and then set to nil in -dealloc.
See Darren's answer at: Settings IBOutlets to nil in dealloc
See an IBOutletCollection example at: http://www.bobmccune.com/2011/01/31/using-ios-4s-iboutletcollection/.
I have an iPhone application that loads succesive views in a framework based on the one explained in this link (basically a main ViewController that loads/removes additional views with a displayView method). In my application I am using NIBs (the example link uses coded views) though so each of my ViewControllers has its accompanying nib.
Debugging in Instruments shows no leaks but if I enter/leave a section (ViewController with its View.xib), the nib remains in memory so after a few in/outs memory starts to accumulate.
I know the nib is not being unloaded because one is almost programmatically created (no stuff in IB) while another does have images and buttons created in IB. The large one is loaded first and the small one loads next. You would expect a reduction in allocation in Instruments.
How can I prevent this?
My structure is as follows, with a few comments below:
`MyAppDelegate.h`
#import <UIKit/UIKit.h>
#class RootViewController;
#interface MyAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
RootViewController *viewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet RootViewController *viewController;
-(void) displayView:(int)intNewView;
#end
`MyAppDelegate.m`
#import "MyAppDelegate.h"
#import "RootViewController.h"
#implementation MyAppDelegate
#synthesize window;
#synthesize viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
}
-(void) displayView:(int)intNewView {
[viewController displayView:intNewView];
}
- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
#end
This controller handles subview load/removes:
`RootViewController.h`
#import <UIKit/UIKit.h>
#interface RootViewController : UIViewController {
}
- (void) displayView:(int)intNewView;
#end
`RootViewController.m`
#import "RootViewController.h"
#import "ViewController.h"
#implementation RootViewController
UIViewController *currentView;
- (void) displayView:(int)intNewView {
NSLog(#"%i", intNewView);
[currentView.view removeFromSuperview];
[currentView release];
switch (intNewView) {
case 1:
currentView = [[ViewController alloc] initWithNibName:#"View" bundle:nil];
break;
}
[self.view addSubview:currentView.view];
}
- (void)viewDidLoad {
currentView = [[ViewController alloc]
initWithNibName:#"View" bundle:nil];
[self.view addSubview:currentView.view];
[super viewDidLoad];
}
- (void)dealloc {
[currentView release];
[super dealloc];
}
#end
There would be as many case as "detail" ViewControllers I have (right now I have 3 case but this will grow to 10 or more). The purpose of this structure is to easily move from one "section" of the application to another (NavBar controller or TabBar controller do not suit my specific needs).
`ViewController.h`
// Generic View Controller Example
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
UIImageView *_image1;
UIImageView *_image2;
NSTimer *_theTimer;
}
#property (nonatomic, retain) IBOutlet UIImageView *image1;
#property (nonatomic, retain) IBOutlet UIImageView *image2;
#property (nonatomic, retain) NSTimer *theTimer;
#end
`ViewController.m`
#import "ViewController.h"
#import "MyAppDelegate.h"
#synthesize image1 = _image1, image2 = _image2, theTimer = _theTimer;
- (void)loadMenu {
[self.theTimer invalidate];
self.theTimer = nil;
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate displayView:2];
}
-(void)setView:(UIView*)aView {
if (!aView){
self.image1 = nil;
self.image2 = nil;
}
[super setView:aView];
}
- (void)viewDidLoad {
//some code
[super viewDidLoad];
}
- (void)viewDidUnload {
self.image1 = nil;
self.image2 = nil;
}
- (void)dealloc {
NSLog(#"dealloc called");
[self.theTimer invalidate];
[self.theTimer release];
[self.image1 release];
[self.image2 release];
[super dealloc];
}
Notice the NSLog in dealloc. This is being called (I can see it in the console) but the memory needed for the nib is not freed (Instruments shows an increase in memory allocation when leaving a section, because a new nib is loaded).
Any help will be greatly appreciated. I have tried a million different things and I cannot get the nibs to unload.
After a million different tries I finally ran into this forum.
It states:
Apparently images assigned in IB are loaded into image views using imageNamed. imageNamed caches the images in a way that makes them unloadable. You could load the images in viewDidLoad with initWithContentsOfFile and then assign them to the views.
Somewhere else I had read that imageNamed is the devil so I'd rather not have my images load that way.
(BTW this is iPhone OS 3.1 I'm using)
What I ended up is leaving the UIImageView intact in IB but with an empty .image value. The modified code is something like:
- (void)viewDidLoad {
NSString *path = [NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], #"myImageThatBeforeWasAValueinIB.jpg"];
UIImage *image = [UIImage imageWithContentsOfFile:path];
outlet.image = image;
// do the rest of my stuff as it was
[super viewDidLoad];
}
- (void)dealloc {
outlet.image = nil;
[outlet release], outlet = nil;
[super dealloc];
}
And now everything works like a charm! Memory is recovered when I unload a nib and when I get memory warnings.
So pretty much if you have IBOutlets for UIImageViews and memory is a concern (it always is I guess), you can design all you want in IB and when the time comes to connect them to outlets, remove the image reference in IB and create it from code. IB is really good for laying out your app. It would suck to have to do all that thing by code, but I also found this nice utility that converts nibs to objective c code although I haven't tested it yet.
Did you try setting your outlet variables to nil in dealloc?
You are correctly implementing the setView method, but you are setting your outlet variables to nil in the viewDidUnload method instead of dealloc. As discussed here, you should implement dealloc as follows:
- (void)setView:(UIView *)aView {
if (!aView) { // view is being set to nil
// set outlets to nil, e.g.
self.anOutlet = nil;
}
// Invoke super's implementation last
[super setView:aView];
}
- (void)dealloc {
// release outlets and set outlet variables to nil
[anOutlet release], anOutlet = nil;
[super dealloc];
}
EDIT: if the outlets are UIImageViews, then it may be the case that you need to do
anOutlet.image = nil;
because setting the UIImage’s instance image property should increase the retain count of the UIImage’s instance by 1.
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.