Advertise Mobile SDKs Books Events Forum News Social Networking Support Us
Follow @iphonedevsdk on Twitter

Interface 2, Advanced iOS
Mockup & Code Gen
($9.99)

Make your own iPhone apps
and run them live!
(free)

Pic Frame Dynamo: Photo Editing
($0.99)

Abiliator
($1.99)

Want your application or service advertised on iPhone Dev SDK?

Go Back   iPhone Dev SDK Forum > iPhone SDK Development Forums > iPhone SDK Development

Reply
 
LinkBack Thread Tools Display Modes
Old 09-13-2008, 03:44 AM   #1 (permalink)
Registered Member
 
Join Date: Sep 2008
Posts: 8
avirzi is on a distinguished road
Post NavigationController -> TableView ->TabBarController

Hi everyone,
I am in serious need of help because I've been bagging my head over this for weeks and I still have a lot of issues with memory leaks and navigations.


The app is based on this navigation scheme:

1. Standard navigation based application with a RootViewController created as a simple view (not a tableView).

2. Pressing a button on the RootView, it performs few operations and then push a tableView on screen.

3. Selecting a row on the table it gets pushed on screen a TabBarController having 4 tabs in it.

Now, I can navigate through the tabs but if I go back to the table and then back again in the TabBarController (selecting any other cell) and navigating again the tabs I get memory leaks which eventually make everything fall apart.


This is what I do to create the elements:

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
// Configure and show the window
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];

[self loadTableViewController];
[self loadTabBarController];
}

where:

- (void)loadTableViewController
{
// ALLOC THE TABLE VIEW CONTROLLER
TableViewController *viewController = [[TableViewController alloc] initWithNibName:@"TableView" bundle:[NSBundle mainBundle]];
self.tableViewController = viewController;
[viewController release];
}


- (void)loadTabBarController
{
// ALLOC THE TABBAR CONTROLLER
self.tabBarController = [[UITabBarController alloc] init];

self.tab1Controller = [[[Tab1Controller alloc] initWithNibName:@"Tab1" bundle:[NSBundle mainBundle]] autorelease];
self.tab2Controller = [[[Tab2Controller alloc] initWithNibName:@"Tab2" bundle:[NSBundle mainBundle]] autorelease];
self.tab3Controller = [[[Tab3Controller alloc] initWithNibName:@"Tab3" bundle:[NSBundle mainBundle]] autorelease];

// Add all of your tabs to the tab bar. You can put as many controllers on as you like, but they will turn into the more button like in the iPod program.
tabBarController.viewControllers = [NSArray arrayWithObjects:tab1Controller, tab2Controller, tab3Controller, nil];

tab1Controller.title = @"TAB 1";
tab2Controller.title = @"TAB 2";
tab3Controller.title = @"TAB 3";
}


To push these views on screen I do:

- (IBAction)pushTableViewController:(id)sender
{
[self.navigationController pushViewController:tableViewController animated:YES];
}


- (IBAction)pushTabBarController:(id)sender;
{
[self.navigationController pushViewController:tabBarController animated:YES];
}


These 2 actions are called, respectively, from the RootViewController and the TableViewController like this:

[[AppDelegate shared] pushTableViewController:self];

[[AppDelegate shared] pushTabBarController:self];


How can I allocate the TabBarController only when I need it and deallocate everything it contains when it disappear?
The leaks appear when I get into a tab from the list, I go other tabs, I go out from a different tab (respect the initial one loaded) and then I get back in.

Any clue?
Also, Is that way to allocate the TabBarController and all the tabs correct?

If I try to alloc all the tabs the same way I alloc the TableViewController (with the temp variable that I dealloc right away) it crashes at launch.


Cheers
avirzi is offline   Reply With Quote
Old 09-13-2008, 05:20 PM   #2 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 1,431
PhoneyDeveloper is on a distinguished road
Default

The code in your loadTabBarController method is partly wrong. It needs to be based on the same pattern as in loadTableViewController

value = alloc/init {implicit retain}
self.value = value {implicit retain}
[value release]

and there needs to be another release somewhere, usually in your dealloc method.

View controllers are retained on pushViewController and released on popViewController.

One other style point. If you have view controllers that load from a nib then they should know their own nib name but no other code needs to know what nib a particular view controller loads itself from. I use code like this

Code:
Mycontroller* controller = [[Mycontroller alloc] init];
and in Mycontroller.m

Code:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 
{
	if ((self = [super initWithNibName:@"MyNibName" bundle:nibBundleOrNil])) 
	{
		// Initialization code
	}
	return self;
}
I think this is cleaner. The nibname is defined in only one place in the code, which makes it easier to change if needed and reduces the effects of typos.
PhoneyDeveloper is offline   Reply With Quote
Old 09-14-2008, 06:18 AM   #3 (permalink)
Registered Member
 
Join Date: Sep 2008
Posts: 8
avirzi is on a distinguished road
Post

Thanks a lot,

I've modified the code so now it is like this:

- (void)loadTabBarController
{
UITabBarController *tabBar = [[UITabBarController alloc] init];
self.tabBarController = tabBar;
[tabBar release];

Tab1Controller *tab1 = [[Tab1Controller alloc] init];
self.tab1Controller = tab1;
[tab1 release];

Tab2Controller *tab2 = [[Tab2Controller alloc] init];
self.tab2Controller = tab2;
[tab2 release];

Tab3Controller *tab3 = [[Tab3Controller alloc] init];
self.tab3Controller = tab3;
[tab3 release];


tabBarController.viewControllers = [NSArray arrayWithObjects:tab1Controller, tab2Controller, tab3Controller, nil];

tab1Controller.title = @"Tab 1";
tab2Controller.title = @"Tab 2";
tab3Controller.title = @"Tab 3";
}


And in the dealloc method:

- (void)dealloc
{
[tab1Controller release];
[tab2Controller release];
[tab3Controller release];
[tabBarController release];


[window release];
[super dealloc];
}


One question though...

The tabs are going to have web views and images and quite a lot of datas and the memory goes up.

Is there a way (best practice) to dealloc the tabs on the popViewController, so the memory stays down.

Especially for the web views that take a looot of memory and don't seem to release it.

What's the best practice on this?
Doing an alloc/release directly (and how) or with the autorelease or in some other way?

Cheers
avirzi is offline   Reply With Quote
Old 09-14-2008, 09:15 AM   #4 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 1,431
PhoneyDeveloper is on a distinguished road
Default

I haven't worked with a tabbar app so I don't know for certain but the basic idea is probably to have each controller load and unload its nib when it is about to appear and disappear. Use viewWillAppear: and probably viewDidDisappear in the tabbarcontrollers to load and unload the content.

You could have a nib that has just a plain UIView in it that the tabbar controllers load from their init method. Then each has another nib that it loads and unloads dynamically using the API in UINibLoading. Once this second nib is loaded you'd need to addSubview the contents of this second nib to the plain view that was loaded from init. You could removeSubview when the view will go away.

You might also be able to have a viewcontroller that controls the 'real' nib and a simpler controller that is in the viewControllers array for each tab. The same idea as above just that the nib loading would be handled by UIViewController. So you'd load the 'real' view controller in viewWillAppear and release it in viewDidDisappear. You'd then addSubview:realViewController.view same as above.

This shouldn't be too complicated and should keep the memory usage to a minimum.

BTW, I'd also modify the way you're creating the viewcontrollers array. Use

[[NSArray alloc] initWithObjects:]
tabBarController.viewControllers =
[array release]

in the same way as the other code.

The strategy is to avoid autoreleased objects that will hang around for a long time. For objects with short lifetimes it's ok to make them as autoreleased objects. Having an object that is stored in the autorelease pool for a long time means that it takes up some memory in the autorelease pool and some processing time each time the pool is drained, even though the object isn't going to be released. So don't use the convenience methods for creating objects that will be long-lived, like the view controllers array.
PhoneyDeveloper is offline   Reply With Quote
Old 09-14-2008, 05:31 PM   #5 (permalink)
Registered Member
 
Join Date: Sep 2008
Posts: 8
avirzi is on a distinguished road
Post

Hi there,

Thanks again for the reply but I need to bother you again unfortunately.
I haven't been able to find any reference to the API UINibLoading.

How should this work?

Do you have an example that explains what you're telling me?

I would really, really appreciated :-)

Cheers
avirzi is offline   Reply With Quote
Old 09-14-2008, 06:32 PM   #6 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 1,431
PhoneyDeveloper is on a distinguished road
Default

It's in UINibloading.h and is part of NSBundle. It's described in the NSBundle documentation and also the Resource Programmming Guide.
PhoneyDeveloper is offline   Reply With Quote
Old 09-14-2008, 06:43 PM   #7 (permalink)
Registered Member
 
Join Date: Sep 2008
Posts: 8
avirzi is on a distinguished road
Default

Hi,

Sorry, You've anticipated me

I've seen the UINibLoading.h.

It contains:
- (NSArray *)loadNibNamedNSString *)name ownerid)owner optionsNSDictionary *)options;

- (void)awakeFromNib;


I've created a method to load my custom WebView from its nib:

+ (MyWebViewController *)newWebWiew
{
NSArray *topObjs = nil;

topObjs = [[NSBundle mainBundle] loadNibNamed:@"MyWebView" owner:self options:nil];

return (MyWebViewController *)[topObjs objectAtIndex:1];
}


I am trying to do something like this:

- (void)viewWillAppearBOOL)animated
{
// myView is an outlet in self
myView = [self newWebWiew];
}


I have no idea if this is what you meant or if I am completely wrong here.

Thanks
avirzi is offline   Reply With Quote
Old 09-14-2008, 07:18 PM   #8 (permalink)
New Member
 
Join Date: Sep 2008
Posts: 1,431
PhoneyDeveloper is on a distinguished road
Default

The resulting code should be very similar to the way that UIViewController loads its nib.

You just need something like this

self.toplevelObjects = [[NSBundle mainBundle] loadNibNamed:@"TestNibLoadingViewController" owner:self options:nil];

When that is called the specified nib is loaded. Since self is passed as owner, self is the file's owner. When this method returns all the objects in the nib will have been created and any outlets set up in IB will be set on self.

You need to retain the toplevelObjects array and then when you want to dispose of the view in viewDidDisappear you removeSubview and self.toplevelObjects = nil (or otherwise release it)

UIViewController does all this automatically so having a second viewController might be simpler.
PhoneyDeveloper is offline   Reply With Quote
Old 09-18-2008, 07:21 AM   #9 (permalink)
Registered Member
 
roberthuttinger's Avatar
 
Join Date: Sep 2008
Posts: 96
roberthuttinger is on a distinguished road
Default

a second view controller? that what Im trying to do. I have a table view in a tabbed app. Im trying to make a text area appear above the table so one could type in a new entry for the table. would I use UINibloading for that? How can I implement 2 controllers on the same window?

this is in my delegate now:
HTML Code:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
	textViewController = [[TextViewController alloc] initWithNibName:@"TextView" bundle:nil];
	[[[[tabBarController viewControllers] objectAtIndex:4] view] addSubview:[textViewController view]];
	tableViewController = [[TableViewController alloc] initWithNibName:@"TableView" bundle:nil];
	[[[[tabBarController viewControllers] objectAtIndex:4] view] addSubview:[tableViewController view]];
In the TextViewController do I need to add code to the 'loadView' function?

update:
so initWithNibName doesnt work apparently with tabcontrollers? so I need to use 'loadView' I think. but when I try to create an IBOutlet for the view in the .h file I get an error about declaring a view as a constant?

cheers.rob

Last edited by roberthuttinger; 09-18-2008 at 08:19 AM. Reason: added code scooby doo
roberthuttinger is offline   Reply With Quote
Reply

Bookmarks

Tags
memory leaks, navigationcontroller, tabbarcontroller, tableview

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On



» Advertisements
» Online Users: 340
14 members and 326 guests
akacaj, alexP, ClerurcifeDer, Domele, Duncan C, givensur, GraffitiCircus, JmayLive, michelle, NetGuru, NSString, Paul Slocum, Sloshmonster, soohyun
Most users ever online was 1,387, 04-10-2012 at 04:21 AM.
» Stats
Members: 175,650
Threads: 94,114
Posts: 402,883
Top Poster: BrianSlick (7,990)
Welcome to our newest member, soohyun
Powered by vBadvanced CMPS v3.1.0

All times are GMT -5. The time now is 09:51 PM.
Powered by vBulletin® Version 3.8.0
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.3.0