iphone #property(retain), init(), and standards - iphone

I'm new to the memory management of the iphone and had a question about standards/correctness.
My header file declares:
IBOutlet UITabBarController *tabBarController;
#property (nonatomic, retain) UITabBarController *tabBarController;
In my init() code I was doing something like the following:
self.tabBarController = [[UITabBarController alloc] init];
[tabBarController release];
NSLog(#"Retain count of tbc: %d",[tabBarController retainCount]);
to get the retain count back to one. Is this correct from a standardization point of view? It just looked a bit different to me, but again I'm new to this.
Thanks

That's normal.
What you do:
self.tabBarController = [[UITabBarController alloc] init];
[tabBarController release];
maybe performed by compiler as:
id *tempVar = [[UITabBarController alloc] init];
self.tabBarController = tempVar; //till now, retainCount is 2
[tabBarController release]; //now, retainCount is 1
When you alloc it, this memory block will be retained by a temporary var. So a better way to do that is:
UITabBarController *tabCtl = [[UITabBarController alloc] init];
self.tabBarController = tabCtl;
[tabCtl release];
I'm not an expert of object-c, just have some knowledge about compiling. So, if I'm wrong, experts here please point out.

For assigning to the property, you should either use
self.tabBarController = [[[UITabBarController alloc] init] autorelease];
or
[tabBarController release];
tabBarController = [[UITabBarController alloc] init];
(Remember to release the previous value first)
The property setters/getters should be solely responsible for retaining/releasing the instance variable. What happens if you (or somebody else) changes the property setter to take a copy of the input instead of retaining it? In that case you are going to over-release the instance variable and leak the original object.
You are essentially sending a message which has private side-effects, and then using knowledge of those private details by releasing the instance variable on the next line. i.e. your code is the same as:
[self setTabBarControler:[[UITabBarController alloc] init]];
/* Relying on knowledge of the instance variable is bad here, setTabBarController
might do something different in the future */
[tabBarController release];
Even though you are in full control of the class, you should still adhere to the basic principles of abstraction and encapsulation. Leave the retaining/releasing of instance variables to the underlying property implementation.

Why not
tabBarController = [[UITabBarController alloc] init];
?

You could do it this way, but better is to not use this implicit setter syntax in your init() method (because you might override the setter and do further stuff that might not work yet if the object is not fully initialized).
Just do:
tabBarController = [[UITabBarController alloc] init];

Related

allocating Memory to the variable which has been already allocated?

I want to allocate memory for the variable to which i have allocated already.for example
self.m_tabbarController = [[TabbarController alloc] init];
I have to change assigned view controller for above tabbar controller.so i have to release
the above and allocate the same tabbar with new controllers. how can I release and allocate
new one.If i do the following, gives crashes.
if(self.m_tabbarController != nil)
{
[self.m_tabbarController release];
}
self.m_tabbarController = [[TabbarController alloc] init];
but self variable must be deallcated in dealloc method.any help please?if i do like following also, it gives crash?
m_tabbarController = [[TabbarController alloc] init];
[self.window addSubview:m_tabbarController ];
[m_tabbarController release];
First release it. Assuming your synthesised property is retain, the implementation will handle the release for you:
self.m_tabbarController = [[[TabbarController alloc] init] autorelease];
In short, you cannot rely on some way to reinitialize an instance unless you implement some kind of reinitialization method in the class.
If that's a UIViewController, just create a new UIViewController because you would need to know a lot about an implementation, all subclasses, and all members/ivars to implement reinitialization correctly. Sometimes you can accomplish this via its public properties, sometimes you won;t be able to reinitialize an instance correctly.
One problem with reinitialization is that what you alloc may not be what's returned -- and you may not otherwise know what type you are dealing with specifically in all cases. Proper, exhaustive reimplementation of a complex type adds a lot of implementation (which tends to be transitive too).
self.m_tabbarController = nil;
TabbarController *tempController = [[TabbarController alloc] init];
self.m_tabbarController = tempController;
[tempController release];
tempController = nil;
You should declare m_tabbarController with retain kind of property.
Now, modify your code as below:
TabbarController *temp = [[TabbarController alloc] init];
self.m_tabbarController = temp;
[temp release];
Also, release m_tabbarController into dealloc method.
[self.m_tabbarController release]; will release m_tabbarController not self so
if(self.m_tabbarController != nil)
{
[self.m_tabbarController release];
}
self.m_tabbarController = [[TabbarController alloc] init];
is absolutely fine

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]

Bad memory leak when creating a view - even though a 'release' is present?

Part of my code presents a UITableViewController in the following way:
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:#"Settings" bundle:nil];
flipside = [[UINavigationController alloc] initWithRootViewController:controller];
controller.delegate = self;
flipside.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:flipside animated:YES];
[flipside release];
[controller release];
Using the leaks tools, no memory leaks are picked up on. However, whenever I bring up the settings menu (as shown in the code above), more memory appears to be allocated and never released - almost 100 kB every time.
Weirdly, the inclusion of the two release statements at the end seems to have no effect on the memory allocation..? Is there something I am misunderstanding about memory allocation in objective-c, or is something weird going on?
Any ideas are much appreciated - thanks!
If flipside is a retained property then the navigation controller is leaking. the problem is that you are bypassing the accessor method and releasing flipside directly. This is just messy code. A better way to do it would be to make an accessor method for flipside that will only alloc a new one if you haven't already created one. It's called lazy loading. To do this, just leave the #synthesize for flipside (but you shouldn't set it from outside the accessor method), in your header file change the property to, and add this method to the implementation:
- (UINavigationController *)flipside {
if (flipside != nil) {
return flipside;
}
FlipsideViewController *controller = [[[[FlipsideViewController alloc] initWithNibName:#"Settings" bundle:nil];
controller.delegate = self;
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
[controller release];
navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
// This implies a retain if your flipside property is set to retain
self.flipside = navController;
[navController release];
}
make sure to put self.flipside = nil in the viewDidUnload method of the view controller the code you included came from (I assume it's a presentSettings action).
what your presentSetting action should now look like is this:
- (IBAction)presentSettings {
// make sure you use the accessor self.flipside instead on accessing the variable directly
[self presentModalViewController:self.flipside animated:YES];
}

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

tabBarController memory leak

In my AppDelegate I initiate a tabBar Controller, to which a bunch of navigationController is added as tabs. I use the following code:
// Init tabBar Controller
tabBarController = [[[UITabBarController alloc] init] retain];
// Init Root Views of navigation controllers
FirstRootViewController* firstViewController = [[[FirstRootViewController alloc] init] autorelease];
SecondRootViewController* secondViewController = [[[SecondRootViewController alloc] init] autorelease];
ThirdRootViewController* thirdViewController = [[[ThirdRootViewController alloc] init] autorelease];
// Init Navigation controllers of tabs
UINavigationController* firstNavController = [[[UINavigationController alloc] initWithRootViewController:firstViewController] autorelease];
UINavigationController* secondNavController = [[[UINavigationController alloc] initWithRootViewController:secondViewController] autorelease];
UINavigationController* thirdNavController = [[[UINavigationController alloc] initWithRootViewController:thirdViewController] autorelease];
firstNavController.navigationBar.barStyle = UIBarStyleBlack;
secondNavController.navigationBar.barStyle = UIBarStyleBlack;
thirdNavController.navigationBar.barStyle = UIBarStyleBlack;
// Create array for tabBarController and add navigation controllers to tabBarController
NSArray *navigationControllers = [NSArray arrayWithObjects:firstNavController, secondNavController, thirdNavController, nil];
tabBarController.viewControllers = navigationControllers;
[window addSubview:tabBarController.view];
And the dealloc function:
- (void)dealloc {
[window release];
[tabBarController release];
[super dealloc]; }
firstNavController are the navigation controllers to be added which are properly released altogether a few lines later (they are created using alloc).
TabBarController is a class variable which has been created using #property (nonatomic, retain) and #synthesize tabBarController. It receives a release command in the dealloc method.
Now instruments tells me that I have two memory leaks on the line with "tabBarController.viewControllers = navigationController".
I have tortured my head, yet I don't see why: From my understanding, navigationControllers should get released automatically and if I send it a release command a few lines later, the app crashes, so I guess I am right.
Any guesses whats wrong?
Thanks a lot!
Firstly, your tabBarController class variable has it's reference count increased twice. Once from the alloc and once from the retain in the first line of your code, yet is only released once in dealloc This is probably where your memory leak is coming from.
Secondly, although you have declared a matching #property(nonatomic, retain) tabBarController (and implemented via #sysnthesize) you are not actually using the property accessors (and its corresponding retain & release behaviour during assignment) To do this you need to use self.tabBarController rather than just tabBarController which will refer to the class variable, not the property.
Try modifying your code to the following to see if this solves your problem
// Init tabBar Controller
UITabBarController* tbc = [[[UITabBarController alloc] init];
self.tabBarController = tbc;
[tbc release];
...
- (void)dealloc {
[window release];
self.tabBarController = nil;
[super dealloc]; }