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
Related
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
I want to navigate from one ViewController to another. As part of this I want to
pass the ViewController I want to navigate to some information. I encapsulated the
information into an Object that I want to hook up as an external object with the target
viewController.
I created the external Object inside IB gave it the identifier I referenced in the NSDictionary that is passed to the NibLoading method.
NSArray* topLevelObjs = nil;
NSMutableDictionary* options = [[NSMutableDictionary alloc] initWithCapacity:1];
NSMutableDictionary* config = [[NSMutableDictionary alloc] initWithCapacity:1];
id detailImageVC = [[SelectedImageModalViewController alloc] init];
SelectedImageModalModel* selectImageModalModel = [[SelectedImageModalModel alloc] init];
selectImageModalModel.imageName = #"img#2x.png";
[config setValue:selectImageModalModel forKey:#"SelectImageModalModel"];
[options setValue:config forKey:UINibExternalObjects];
topLevelObjs = [[NSBundle mainBundle] loadNibNamed:#"SelectedImageModalViewController" owner:detailImageVC options:options];
if ([topLevelObjs count] == 0)
{
NSLog(#"Warning! Could not substitute proxy objects in xib file.\n");
return;
}
[appDelegate.navigationController presentModalViewController:detailImageVC animated:YES];
[options release];
[config release];
[selectImageModalModel release];
[detailImageVC release];
What I expected was, that after I called presentModalViewController:animated: I would receive a call to viewDidLoad on the very same detailImageVC with my external objects
connected.
Instead neither happens. viewWillApear: will get called, but the detailImageVC won't hold my external reference.
Any idea, hind or comment is appreciated. Thanks!
viewDidLoad will be called if only ViewController loaded the view itself.
For example; initWithNibName does not load the view, it just sets the nib name. When ViewController needs its view in some future point and if there is no view in ViewController.view then ViewController will load view like you did AND THEN INVOKE viewDidLoad itself.
Your code loads the view of ViewController itself. So you should call the viewDidLoad method in your code like this:
topLevelObjs = [[NSBundle mainBundle] loadNibNamed:#"SelectedImageModalViewController" owner:detailImageVC options:options];
if (topLevelObjs.count == 0) {
NSLog(#"Warning! Could not substitute proxy objects in xib file.\n");
return;
} else {
[detailImageVC viewDidLoad];
}
If your detailImageVC does not hold your external object then you should check your nib file for IBOutlet bindings and your SelectedImageModalViewController for corresponding #property. If property is not strong like #property(nonatomic, strong) on ARC, or, is not reatin on Non-ARC like #property(nonatomic, retain) then it will not hold your object after awaking from nib.
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]
I'm reading from AppDelegate not small array.
NSArray *mycountryspecific = [[NSArray alloc] initWithContentsOfURL:[[NSBundle mainBundle] URLForResource:#"nextTwoWeekEvents" withExtension:#"ary"]];
self.myCountrySpecificCodesList = mycountryspecific;
[mycountryspecific release],mycountryspecific = nil;
To keep it in memory, i declared property
#property (nonatomic, retain) NSArray *myCountrySpecificCodesList;
But from main view i have to go 2 steps while i will using it, under event controller i will have event detailed view controller where it will be necessary part of this array (based on predicate).
My current design is a read array not from AppDelegate, but from event detailed view controller when it necessary.
Can u share u experience? If better is load array from appdelegate and keep in memory, please advice a correct way to send access property between classes without memory leaks.
EventsTableViewController *events = [[EventsTableViewController alloc] initWithNibName:#"EventsTableViewController" bundle:nil];
self.eventListController = events;
PromoView *promo = [[PromoView alloc] initWithNibName:#"PromoView" bundle:nil];
UINavigationController *navigationController = [[UINavigationController alloc] init];
[navigationController pushViewController:events animated:NO];
UITabBarController *tabBar = [[UITabBarController alloc] init];
self.tabBarController = tabBar;
[tabBar setViewControllers:[NSArray arrayWithObjects:navigationController,promo,nil]];
eventListController.managedObjectContext = self.managedObjectContext;
[self.window addSubview:tabBarController.view];
SOLUTION
After some playing with my controllers, i was find a solution.
I still read array from file everytime when i showing a detailed page, but now this is a dictionary, where a key is a first world to find and array for this cay is a result which i need.
Array was transer to 34k and a code, which make a case intensive search in keys is:
NSDictionary *mycountryspecific = [[NSDictionary alloc] initWithContentsOfURL:[[NSBundle mainBundle] URLForResource:#"countryspecific" withExtension:#"dict"]];
NSSet * mySet = [mycountryspecific keysOfEntriesPassingTest:^(id key, id obj, BOOL *stop) {
if([key compare:countryName options:NSCaseInsensitiveSearch] == NSOrderedSame) {
return YES;
}
else
return NO;
}];
NSArray *result = [mycountryspecific valueForKey:[mySet anyObject]];
This way is working well on iphone 3G :) and of course, much better on emulator ;-)
Rather than using one massive array that's held entirely in memory, I'd suggest storing your list of country-specific codes in a Core Data database.
By doing so, you could use batched fetches and an NSFetchedResultsController to only load whatever information you needed to present to the user at that given moment and nothing more. This will significantly reduce your application's overall memory usage, and will probably lead to faster startup and load times.
Each view controller could then have its own NSFetchedResultsController instance for accessing the database, providing different perspectives on the data.
I use singleton class to store shared data . It's very Ņonvenient. Take a look at this template http://blog.mugunthkumar.com/coding/xcode-tip-singleton-class-template/
If you retain and release your memory properly, it shouldn't matter where the object is initialized. Just ensure that all your properties are retain, and you enforce a release call in the dealloc for each class that retains.
Something I run into a lot is not being able to create and destroy a ViewController properly when adding the ViewController.view as a subview not on a navigation controller.
for example:
MyViewController *myViewController = [[MyViewController alloc] init];
[currentView addSubView:myViewController.view];
[myViewController release];
That Works great if it is a controllerless view and there are no UIControls that the user must interact with. But sending messages to the view controller of that view causes EXEC_BAD_ACCESS because they are no longer in memory.
MyViewController *myViewController = [[MyViewController alloc] init];
[currentView addSubView:myViewController.view];
This works when sending messages, however it is a memory leak and is caught by the static analyzer.
Setting it as a property of the current view controller works sometimes. But if I need to create a bunch with an unknown number of MyViewControllers and add them to something like a UIScrollView, that doesn't work either.
for (int i = 0; i < [myViewControllers count]; i++) {
MyViewController *myTmpViewController = [[MyViewController alloc] init];
[myCurrentUIScrollView addSubview:myTmpViewController.view];
[myTmpViewController release];
}
Still gonna crash if myTmpViewController has user interaction or something similar. How does one go about adding this, and releasing it properly?
You can have a NSMutableArray and add the controllers there.
for (int i = 0; i < [myViewControllers count]; i++) {
MyViewController *myTmpViewController = [[MyViewController alloc] init];
[myCurrentUIScrollView addSubview:myTmpViewController.view];
[myControllers addObject:myTmpViewController];
[myTmpViewController release];
}
// ..
- (void) dealloc {
[super dealloc];
[myControllers release];
}
You could store a pointer to the view controller in an ivar and then release it in your dealloc method.
If such a subview has limited 'controlling needs', then you might consider to subclass from UIView and have the view control itself (e.g. Be its own delegate)
Otherwise you need to decide for the most logical 'owner' of these viewcontrollers (often the viewcontroller of the parentview) and make them ivars of their owner.