Why is the retain count so high? Memory Management - iphone

I have been going back through my app trying to handle all the memory problems and reading up on memory management.
I began using [object retainCount] to trace my memory allocation.
Is this to be trusted because I keep finding the counts really strange?
Could someone explain the following:
Bear in mind this the app delegate and an empty mainViewController makes no difference.
The initWithRootViewController is causing the count to go up, but I don't see another way of adding one....
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/* Create the View Controllers */
UIViewController *mainViewControl = [[[MainViewController alloc] init] autorelease];
/* Create the Navigation Controller */
UINavigationController *navigationController = [[[UINavigationController alloc] initWithRootViewController:mainViewControl] autorelease];
NSLog(#"retain count: %i",[mainViewControl retainCount]);
/* Set the toolbar to purple */
navigationController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
navigationController.navigationBar.tintColor = [UIColor colorWithRed:.6 green:.1 blue:.4 alpha:0.4];
navigationController.navigationBar.translucent = YES;
NSLog(#"retain count: %i",[mainViewControl retainCount]);
navigationController.toolbar.barStyle = UIBarStyleBlackTranslucent;
navigationController.toolbar.tintColor = [UIColor colorWithRed:.6 green:.1 blue:.4 alpha:0.4];
navigationController.toolbar.translucent = YES;
[navigationController setNavigationBarHidden:YES animated:NO];
[navigationController setToolbarHidden:YES animated:NO];
NSLog(#"retain count: %i",[mainViewControl retainCount]);
[window addSubview:[navigationController view]];
NSLog(#"retain count: %i",[mainViewControl retainCount]);
And this is the log ~
2011-01-17 19:47:21.278 ANA[5653:207] 3
2011-01-17 19:47:21.282 ANA[5653:207] 4
2011-01-17 19:47:21.286 ANA[5653:207] 7
2011-01-17 19:47:21.287 ANA[5653:207] 12
2011-01-17 19:47:21.301 ANA[5653:207] Load View
I don't understand why changing those properties or referencing the navigationController is causing the retain count to shoot up.
I have done it without the autoreleases and manually released too but the result is the same.
Basically I don't get it, and wonder if the retainCount command is reliable, because if I can't understand this, I don't think I can debug any memory issues elsewhere...

As stated in the official documentation for -retainCount,
Important: This method is typically of no value in debugging memory management issues. Because any number of framework objects may have retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of deferred releases on an object, it is very unlikely that you can get useful information from this method.
Don’t rely on -retainCount. Other objects may be retaining your object without you knowing it, and autoreleased objects might give you a wrong impression of the actual retain count.
You can rely on Apple’s framework objects to do the right thing and relinquish ownership of your object when appropriate, and you need to make sure you are doing that as well.

Related

Xcode Instruments Leak coming from UIDeviceRGBColor

I have a leak coming from UIDeviceRGBColor. The responsible frame is +[UIColor allocWithZone:]. I am not using ARC.
The leak is coming from the method below.
- (void) lAction
{
MKCoordinateRegion mapRegion;
mapRegion.center = mapView.userLocation.coordinate;
mapRegion.span.latitudeDelta = 0.05;
mapRegion.span.longitudeDelta = 0.05;
[mapView setRegion:mapRegion animated: YES];
SettingsViewController *settingsViewController = [[SettingsViewController alloc]
initWithNibName:#"SettingsViewController" bundle:nil];
The leak is coming from this next line:
[self presentModalViewController: settingsViewController animated:YES];
Then the method finishes like this:
self.navigationController.navigationBar.tintColor = [UIColor colorWithRed:40.0/255.0
green:43.0/255.0 blue:46.0/255.0 alpha:1.0];
}
Anyone know how to fix this? Thank you all!
Try:
SettingsViewController *settingsViewController = [[[SettingsViewController alloc]
initWithNibName:#"SettingsViewController" bundle:nil] autorelease];
In order to satisfy the commenter, the explanation is simply that if you're not using ARC, whenever you call alloc, the retain count of the returned object is set to 1. You are responsible for releasing it. One easy way to do that is call autorelease on it which will automatically release it at the end of the main run loop (unless you're managing your own autorelease pool, but I won't get into that). You do want to make sure that as long as you need to use an object your code has something retaining it, in this case when you call
[self presentModalViewController: settingsViewController animated:YES];
an extra retain is called on the settingViewController, so you don't have to worry about it getting dealloced when your method finishes.
I find memory management in Objective-C pretty easy, but it does require extra code and everyone uses ARC these days. If you're just making a few small changes to an existing code base, there is no need to switch to ARC, but if you're going to continue working with the code base for some time, it will be more time efficient to switch. It is pretty straightforward to do, as Xcode will do most of the work for you. (See here: http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html).

I'm having five allocations for an object with only one alloc in the same function

I'm writing a function that only displays a menu (composed by a tableView) in a popOver.
This is the source code:
-(void)pushSearch:(NSString *)option type:(int)optionType
{
searchNav = [[iNavigation alloc] initWithNibName:#"iNavigation" bundle:nil] ;
//This is the UIViewController with the tableView
[searchNav setSearchMode:optionType];
searchNav.view.frame =CGRectMake(0,0,300,600);
NSLog(#"Retain Count: %d",[searchNav retainCount]);
//At this point retain count is 1
if ([pop isPopoverVisible])
{
[pop dismissPopoverAnimated:YES];
[pop release];
}
pop = [[UIPopoverController alloc] initWithContentViewController:searchNav];
NSLog(#"Retain Count: %d",[searchNav retainCount]);
//At this point retain count is 2
[pop presentPopoverFromRect:btnMenu.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[pop setPopoverContentSize:CGSizeMake(350,600)];
NSLog(#"Retain Count: %d",[searchNav retainCount]);
//At this point retain count is 5!!!
[searchNav release];
}
The problem that I'm having is that the used memory to load the tableview is never released. My app grows and grows in memory until crashes.
Why if only i'm making one allocation for searchNav, after assign it to the popOver the reatin count is 5?
Please any help?
Do not use -retainCount.
The absolute retain count of an object is meaningless.
You should call release exactly same number of times that you caused the object to be retained. No less (unless you like leaks) and, certainly, no more (unless you like crashes).
See the Memory Management Guidelines for full details.
That the retain count is 5 is irrelevant and quite likely because of internal implementation details of the various frameworks.
The memory management of searchNav in that code is correct; you allocated the object (+1 retain) and then you eventually released it (-1 retain). Therefore, the memory leak is somewhere else.
Try using "build and analyze" on your source. Then use the Allocations instrument to have a look at the objects that are hanging about as your app grows over time. There is an over-retain somewhere; a retain for which you haven't balanced it with a release.
Do you release the UIPopoverController ? Perhaps that object is still retaining the table view.

Is it ok to autorelease UIViewControllers?

I have some view controllers:
StockTwitsTVViewController* stvViewController = [[[StockTwitsTVViewController alloc] initWithNibName:#"StockTwitsTVViewController" bundle:nil]autorelease];
UINavigationController *stvNavController = [[UINavigationController alloc] initWithRootViewController:stvViewController];
stvNavController.tabBarItem.image = [UIImage imageNamed:#"today-icon.png"];
ScheduleViewController* scheduleViewController = [[[ScheduleViewController alloc] initWithNibName:#"ScheduleViewController" bundle:nil]autorelease];
scheduleViewController.title = #"Archives";
UINavigationController *scheduleNavController = [[UINavigationController alloc] initWithRootViewController:scheduleViewController];
scheduleNavController.tabBarItem.image = [UIImage imageNamed:#"archived-icon.png"];
stvTabBarController = [[UITabBarController alloc] init];
stvTabBarController.delegate = self;
stvTabBarController.viewControllers = [NSArray arrayWithObjects:stvNavController, scheduleNavController, nil];
stvTabBarController.selectedViewController = stvNavController;
[stvNavController release];
[scheduleNavController release];
[self presentModalViewController:stvTabBarController animated:YES];
Is it ok to auto-release them or is it better practice to manually release? Why?
What you do in your code is perfectly fine. To make things more consistent I would also create stdNavController and scheduleNavController as autoreleased objects.
Read mikeash.com: Autorelease is Fast.
What he didn't test was autorelease versus release. When I tested, a million [[[NSObject alloc] init] autorelease] plus the autorelease pool overhead took on the order of twice as long as [[[NSObject alloc] init] release]. Admittedly, I tested on 10.6 (presumably if it's still refcounted since I didn't enable GC), but the relative performance still holds.
Maybe autorelease uses a couple of microseconds of CPU time, but it sure beats adding a memory leak because you changed an ivar to a local, or you copy-pasted code around and forgot the release.
Care about performance when it matters. When it does, you might decide to use CFString instead of NSString and ivar access instead of property access and function calls instead of class methods. In general, though, it's important to write code that is easy to maintain, even if that means using 1% more CPU.
The autorelease will let the release to be done later by the OS, it means when the current run loop finishes.
The release inform the OS that the object id not necessary anymore and that the allocated memory can be freed.
For performance on iOS it's better to use release, instead of autorelease because the system can claim back the allocated memory straightaway.

Why is this code producing an memory leak?

The Leaks Instrument in Xcode shows me an memory leak here. I have commented the affected line which Leaks is complaining about. But I see no error in my memory management...
- (void)setupViewController {
MyViewController *myVC = [[MyViewController alloc] init];
UITabBarItem *tbi = [[UITabBarItem alloc] initWithTabBarSystemItem:UITabBarSystemItemDownloads tag:1];
myVC.tabBarItem = tbi; // LEAK: 128 bytes
self.myViewController = myVC;
[myVC release];
[tbi release];
}
I mean... tbi and myVC IS released at the end, and the alloc IS balanced. So what's wrong? I don't get it.
if MyVc.tabBarItem is already set, whatever it's pointing at may not be deallocated properly, causing a leak.
It just goes to show that at least one of the following statements is true:
Instruments is not perfect and sometimes shows leaks where there aren't any (and vice versa).
Apple's code is not bug-free.
In fact, both are true.

Why does -[NSObject retainCount] sometimes return unexpected values?

Within tableView:cellForRowAtIndexPath:
// Make a cell:
UITableViewCell *cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:#"Default"] autorelease];
// Make a spinner:
UIActivityIndicatorView *spin = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
// [spin retainCount] = 1
// Start spinning
[spin startAnimating];
[cell.contentView insertSubview:spin atIndex:0];
// [spin retainCount] = 3. Huh?
// I would have expected it to be 2 at this point.
[spin release];
// [[cell.contentView.subviews objectAtIndex:0] retainCount] = 2
At this point I would have thought cell.contentView.subview is the sole object with a retain on the spinner. But clearly a retain count of 2 indicates that something else is also retaining it. Could someone please explain to me who the mystery object is that has a retain on the spinner besides cell.contentView's subview array?
Cheers,
Doug
The mystery object that retains your spinner (and lot's of other objects) is called NSAutoreleasePool.
In Cocoa, it's quite useless to look at an objects retainCount, since you cannot know if the object has been retain-autoreleased before (often many times). The retainCount will only decrement after the current pool is drained, usually after the next pass through the run loop. If you do not retain the object it will be deleted when the pool is drained.
So again, the basic rule of thumb is: Do not look at the retainCount.
If there was an autoreleaseCount method it would be possible to calculate the "logical" retain count, but I'm afraid Apple's engineers left this as an exercise for beginning Cocoa developers ;)