Another memory management query for ivars - iphone

There is undoubtedly a wealth of information re: memory management in iOS. Having read a huge amount about it, I am still unclear as to 'BEST' practice for certain situations. Please can I seek clarification on two examples below...
I have an NSMutableArray which is acting as a datasource for a tableView and a UIBarButtonItem called editButton both declared as follows:
#interface MyTableViewController : UITableViewController {
NSMutableArray *datasourceArray;
UIBarButtonItem *editButton;
}
#property (nonatomic, retain) NSMutableArray *datasourceArray;
#property (nonatomic, retain) UIBarButtonItem *editButton;
#end
I have then synthesized them and alloc'd/init'd them as follows:
#implementation
#syntesize datasourceArray, editButton;
-(void)viewDidLoad {
self.datasourceArray = [self retrieveDatasourceArray];
self.editButton = [[UIBarButtonItem alloc] initWithTitle:#"Edit" style:UIBarButtonItemStylePlain target:self action:#selector(editTable)];
[self.navigationItem setRightBarButtonItems:[NSArray arrayWithObjects:editButton, nil] animated:NO];
[editButton release];
}
-(void)retrieveDatasourceArray {
NSMutableArray *datasource = [[[NSMutableArray alloc] initWithObjects #"example1", #"example2", nil] autorelease];
return datasource;
}
-(void)dealloc {
[datasourceArray release];
[editButton release];
[super dealloc];
}
Question 1: The NSMutableArray
As you can see, I have separated the actual creation of the array into a different method as there is lots of code retrieving from core data and sorting going on (not needed for this question) which I wanted to separate out. I have therefore chosen to return an NSMutableArray which is autoreleased and set this to the self.datasourceArray which is defined in the header file. Is this a sensible and leak free way of implementing this?
Question 2: The Edit Button
As I need to change the title and style of the editButton later, I need to have access to it, hence declaring it. I then alloc/init it in the viewDidLoad method and added it to an array (with some other buttons not shown here) before using this array to add the buttons to the navigationBar. I have then released the editButton as I have alloc'd it and handed it to an array. Is this necessary or essential or even in the correct place given my dealloc method?
Many thanks in advance
EDIT: Further question 3:
When accessing either of these ivars elsewhere in my code (say when calling [datasourceArray count] or resetting the title of the 'Edit' button to 'Done', should I use self. notation or not?
EDIT: Further question 4:
Elsewhere I have used the following code to initialise a synthesised NSMutableArray. Given the below answers, is this more leaky...?
[self setDatasourceArray: [[NSMutableArray arrayWithArray: [self retrieveDatasourceArray]];

1st point for the Array
NSMutableArray *datasource = [[[NSMutableArray alloc] initWithObjects #"example1", #"example2", nil] autorelease];
return datasource;
here you are doing it correct..returning an autoreleased object..which will be retained by the variable because you defined it to be of type retain (when you did #property).
2nd point for the edit button
self.editButton = [[UIBarButtonItem alloc] initWithTitle:#"Edit" style:UIBarButtonItemStylePlain target:self action:#selector(editTable)];
[self.navigationItem setRightBarButtonItems:[NSArray arrayWithObjects:editButton, nil] animated:NO];
[editButton release];
Here you are apparently over releasing the object..
remember the variable retains the new variable you defined..so edit button retains the new bar button item..so releasing it . is necessary one time..which you do in dealloc..but releasing here also will lead to overrelease..to solve this just remove the release line and update your code to be like this
self.editButton = [[[UIBarButtonItem alloc] initWithTitle:#"Edit" style:UIBarButtonItemStylePlain target:self action:#selector(editTable)]autorelease];
[self.navigationItem setRightBarButtonItems:[NSArray arrayWithObjects:editButton, nil] animated:NO];
Here you see that the new instance which will be created will be auto released..and its value will be retained by your variable

Related

UIBarbutton item allocation leak - iphone

I am declaring a property for the rightbutton toolbar and allocating it like this
if(self.toolBar == nil)
self.toolBar = [[UIBarButtonItem alloc] initWithCustomView:tools];
self.navigationItem.rightBarButtonItem = self.toolBar;
- (void)viewDidUnload {
toolBar = nil;
}
- (void)dealloc {
[toolBar release];
[super dealloc];
}
When I come to this screen for the second time(2nd time viewDidLoad called), the UIBarbuttonItem is leaking according to Instruments. What could be wrong?
Thanks for all your help in advance.
You're niling the toolbar property prematurely - viewDidUnload will be called before dealloc, thus dealloc will have no chance to release the barbutton, because toolBar points to nil, not the object (which will remain owned (release count of at least 1) but without reference).
Plus, since you're not using the dot notation (self.toolBar) to nil out the property, the old objects release count would not be decreased! So its retain count is at least 2 at the time your controller will quit.
I'd release the object right after assigning it to your property, because the setter method has retained it anyway (if you choose to retain it in the declaration). Later in viewDidUnload all you need to do is self.toolBar = nil; to really get rid of it.
I guess your property is something like this?
#property (nonatomic, retain) IBOutlet UIBarButtonItem toolBar;
This will perform a retain automatically for you but you are giving your property an already retained toobar item.
Try this instead :
if(toolBar == nil)
toolBar = [[UIBarButtonItem alloc] initWithCustomView:tools];
self.navigationItem.rightBarButtonItem = self.toolBar;
If you don't use self. it won't use the property and therefor won't have that extra retain :)

Using self dot notation doesn't call dealloc

The dealloc method of my view controller class is not called when popped after being pushed with the following code:
self.playerViewController = [[VideoPlayerViewController alloc] init];
[self.playerViewController set_video:video];
[self.navigationController pushViewController:self.playerViewController animated:YES];
[self.playerViewController release];
However if I change the push code to the following, then my dealloc is called appropriately after the view controller is popped:
playerViewController = [[VideoPlayerViewController alloc] init];
[playerViewController set_video:video];
[self.navigationController pushViewController:playerViewController animated:YES];
[playerViewController release];
I thought I understood the use of dot notation/self, but obviously not. Can anyone explain the problem here?
Here is the property:
#property (nonatomic, retain) VideoPlayerViewController *playerViewController;
and here is the synthesize:
#synthesize playerViewController;
You are retaining twice.
self.playerViewController = [[VideoPlayerViewController alloc] init];
^ retain + 1 ^^^^^ retain + 1
But you only release one time.
To fix your memory management issue you could change the code to something like this:
self.playerViewController = [[[VideoPlayerViewController alloc] init] autorelease];
And many people say [self.foo release] is bad style. You should consider to replace it with [foo release]

Problem With Editing TableView Code

I have made a table containing an array editable (you can delete rows) by swiping and clicking delete. However, I am trying to make it so you click the edit button in the navbar and the red minus sign comes up next to each cell.
I am using the code from my book but the tableView variable is not working. I can't figure out how it is working in the book but not in my project.
I think it is because the book's class is a subclass of UITableViewController while mine is a UIViewController with a UITableViewController Object in it. So how can I get this to work?
I have an IBOutlet for tableView in my interface file too.
Here is the relevant code:
#import "RoutineTableViewController.h"
#import "AlertPrompt.h"
#implementation RoutineTableViewController
#synthesize myArray;
#synthesize myData;
- (void)viewDidLoad
{
myArray = [[NSMutableArray alloc] init];
myData = [[NSMutableArray arrayWithContentsOfFile:#"mydata"] retain];
if (myData == nil)
{
myData = [NSMutableArray array];
}
UIBarButtonItem * addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(showPrompt)];
[self.navigationItem setLeftBarButtonItem:addButton];
[addButton release];
UIBarButtonItem *editButton = [[UIBarButtonItem alloc]initWithTitle:#"Edit" style:UIBarButtonItemStyleBordered target:self action:#selector(toggleEdit)];
self.navigationItem.rightBarButtonItem = editButton;
[editButton release];
[super viewDidLoad];
}
-(IBAction)toggleEdit:(id)sender
{
[self.tableView setEditing: !=self.tableView.editing animated:YES];
if (self.tableView.editing)
[self.navigationItem.rightBarButtonItem setTitle:#"Done"];
else
[self.navigationItem.rightBarButtonItem setTitle:#"Edit"];
}
You need to set the editingStyle of each UITableViewCell to be UITableViewCellEditingStyleDelete. This should be done when creating the cell within the table view's delegate (tableView:cellForRowAtIndexPath:)
This might look like I am assuming you are a total newbie, but trust me, many people forget this (I'm not saying I'm an expert either), but you should check if your edit button is connected to the corresponding IBOutlet in Interface Builder.
EDIT: Forgot to ask, is your button an IBOutlet (IB element)? Or is it created programmatically? If the second is true, then you should NOT use IBActions for programmatically created objects (hence Interface Builder Action = IBAction)

Memory management on the array initialized for UITabBarController

I am still learning the ropes of Objective C and iPhone development. My weak point is memory management - any help will be much appreciated.
In the code below, where can I release the NSMutableArray listOfViewControllers? Keep in mind the function createTabs can be called within the app multiple times and the tabs are recreated dynamically based on user input. This function is within a ViewController.
If i do [listofViewControllers release] just before exiting the function, the app crashes when I have to call createTabs again
If I use a convenience method like below:
NSMutableArray *listOfViewControllers = [NSMutableArray arrayWithCapacity:1]
instead of:
NSMutableArray *listOfViewControllers = [[NSMutableArray alloc] init]
it still crashes when createTabs is called again
-(void) createTabs
{
//TODO - memory management - where do you release this?
NSMutableArray *listOfViewControllers = [[NSMutableArray alloc] init];
if ([briefingsArray count] > 0)
{
//add briefing(s) tab(s)
for (Briefing *briefing in briefingsArray)
{
WebViewController *briefingViewController = [[WebViewController alloc] initWithBriefing: briefing];
[listOfViewControllers addObject:briefingViewController];
[briefingViewController release];
}
[listOfViewControllers addObject:alertViewController];
//add archives tab
NSString *archiveURL = [NSString stringWithFormat: ARCHIVEURL, DeviceID()];
UIViewController *archiveViewController = [[WebViewController alloc] initWithURL:ARCHIVEURL andTitle:#"Archives" andImage:#"archive_icon.png"];
[listOfViewControllers addObject:archiveViewController];
[archiveViewController release];
}
NSArray *oldlistOfViewControllers = [self.tabBarController viewControllers];
UIViewController *vcOld = [oldlistOfViewControllers objectAtIndex:[oldlistOfViewControllers count] -1];
[listOfViewControllers addObject:vcOld];
[self.tabBarController setViewControllers:listOfViewControllers
animated:YES];
}
My best guess is that it has nothing to do with tab bar controller. When you did not release the array, the controllers in the array would never be dealloc and there was no problem at all. So it's likely that the problem might come from deallocation of your WebViewController.
It looks like what you are creating here - listOfViewControllers - should be an instance variable of whatever object you are making here. Then you should alloc/init it inside the object's -init method, and release it in dealloc.
It is good practice (and usually necessary) to make an instance variable for anything you expect to exist after the end (or before the start) of a function call.
After [self.tabBarController setViewControllers:listOfViewControllers
animated:YES];, you can release your listOfViewControllers. Because tabBarController will retain this listOfViewControllers by copy policy.
You can see the UITabBarController reference about viewControllers property. This property adopts the copy policy.
#property(nonatomic, copy) NSArray *viewControllers

Accessing UISegmentedcontrol selectedIndex - when contained in a UIButton contained in the UINavigationController.toolbar

I am currently adding a UISegmentedControl to the toolbar in the navigation controller programmatically (as seen below).
This approach works fine, I have my UISegmentedControl, it fires the selector that I have setup no problems.
Problem is - I would like to use the selectedIndex of this control in order to query my data model and present a different view of data for each 'segment' - but I am having trouble getting the selectedIndex.
In my travels I have been consulting the 'Top Songs' example code provided by Apple.
In this code they build up their interface via UISegmentedControl object in the view controller and IB. In doing so they can access the UISegmentedControl's selectedIndex. I am adding mine programmactically and do not have this freedom.
'SHOULD' I have a UISegmentedControl defined in my view controller? If so, if I want to continue building my menu programmactically, how do I go about accessing the information from the control buried within the navigation controller's UIToolBar?
I'm clearly missing something basic. Any assistance is always greatly appreciated :)
[self.navigationController setToolbarHidden:NO];
// Set up the edit and add buttons.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(insertNewObject)];
self.navigationItem.rightBarButtonItem = addButton;
[addButton release];
NSArray *tabitems = [NSArray arrayWithObjects:#"ONE", #"TWO", #"THREE", #"FOUR", nil];
UISegmentedControl *tabs = [[UISegmentedControl alloc] initWithItems:tabitems];
[tabs addTarget:self
action:#selector(pickedSegment:)
forControlEvents:UIControlEventValueChanged];
tabs.segmentedControlStyle = UISegmentedControlStyleBar;
tabs.frame = CGRectMake(60, 8, 180, 30);
tabs.selectedSegmentIndex = 0;
//[self.navigationController.navigationBar addSubview:tabs];
[self.navigationController.toolbar addSubview:tabs];
[tabs release];
You need to have tabs defined in your .h file -
#interface YourViewController : UIViewController
....
UISementedControl *tabs;
....
#end
....
#property (nonatomic, retain) UISegmentedControl *tabs;
Then, after the [tabs release]; line, you should still be able to access the object, as it is a retained property - access the selectedItemIndex as normal.