Why is this code producing an memory leak? - iphone

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.

Related

Why is the retain count so high? Memory Management

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.

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 Instruments reporting memory leaks in this code?

Quick question, Instruments is reporting a leak here...
MyViewController *myVC = [[MyViewController alloc] initWithNibName:#"myView" bundle:nil];
[self.navigationController pushViewController:myVC animated:YES]; //<<<<---- !00% leak according to Instruments
[myVC release];
I understand the myVC is retained by the nav controller, so I assume the nav controller releases them when the view is popped off the nav stack?
Also, there's another tricky one in one of my loops, the static analyzer is reporting a potential leak here...
//Walk through the scheduled alarms and create notifications
NSMutableArray *fireDates = [[NSMutableArray alloc] init];
for(NSDate *fireDate in fireDates) //<<<<---- Static analyzer is reporting potential leak here
{
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil)
{
[fireDates release];
return;
}
localNotif.fireDate = fireDate;
localNotif.timeZone = [NSTimeZone defaultTimeZone];
localNotif.alertBody = [NSString stringWithFormat:#"%#", alarm.Label];
localNotif.alertAction = NSLocalizedString(#"Launch", nil);
localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotif.userInfo = infoDict;
localNotif.repeatInterval = NSWeekCalendarUnit;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
[localNotif release];
}
[fireDates release];
Do I need to somehow release fireDate?
Thanks in advance for your help!
These snippets are both fine, as snippets go. Without seeing your full code it's impossible to say if you don't do something silly elsewhere. Do you ever release your navigationController (in your app delegate -dealloc, likely)? That's a leak that doesn't mean much, but it could be what is triggering the first warning.
Edit: with regards to the second snippet, the code looks fine (though the shortcut return case will bother some coders, who would rather see a break statement). The static analyzer may be bothered by the lack of a [localNotif release] (even though it's obviously unnecessary) in the conditional return.
In the first snippet, what is being leaked? Instruments will tell you the line where it was allocated, which is often not the line "responsible" for the leak (of course, line numbers tend to be off because it gives you the return address, not the calling address). I'm assuming it's MyViewController being leaked, and that instruments is actually complaining about alloc (look in the backtrace, cmd-E I think).
If you click on the arrow next to the memory address (you might have to click around a bit and hover over the leak address; I don't remember) you'll see all the alloc/retain/release/autorelease/malloc/free/CFRetain/CFRelease calls on that address. Ignore the ones before alloc (those are for a previous object which happened to live at the same address). Match retains and (auto)releases. You can match an autorelease with the corresponding (delayed) release, because the delayed release will happen when NSAutoreleasePool is release (which is evident in the stack trace). Look for a retain without a matching (auto)release. That's the leak.
In the second case, it helps if you tell us what Clang SA is telling you (click the little triangle on the left and it expands to show you the control-flow where the leak occurs).
But I don't think the loop runs at all, because fireDates is empty.

iPhone memory leak help

This code is leaking, the performance tool blames two leaks on this block of code. If i comment it out the leak doesn't happen. Any help pinning it down would be greatly appreciated.
Leaks:
Malloc 48 bytes
NSCFarray 32 bytes
Code Block:
NSArray *myArray = [[NSArray alloc] initWithObjects: #"Add", #"Edit", nil];
segmentControl = [[UISegmentedControl alloc] initWithItems:myArray];
[myArray release];
[segmentControl setSegmentedControlStyle:UISegmentedControlStyleBar];
[segmentControl setMomentary:YES];
[segmentControl addTarget:self action:#selector(addOrEditPressed) forControlEvents:UIControlEventValueChanged];
UIBarButtonItem *myBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:segmentControl];
self.navigationItem.rightBarButtonItem = myBarButtonItem;
[myBarButtonItem release];
As long as segmentControl is nil when you enter the code block and is being released somewhere else in your code (like dealloc or viewDidUnload) then you are doing nothing wrong.
Have you tried running your code under the static analyzer (Xcode menu: Build | Build & Analyze)?
Instruments can sometimes generate false positives when searching for leaks. If the leaked memory doesn't accumulate over time, your worst case scenario is that your program is leaking a total of 80 bytes. Leaks that grow over time are what you should be concerned about.
Is segmentControl meant to be released?
myArray's retain count is still one after this section of code. When you add it to the initWithItems to create the segmentControl, it now has a reference to the object.
Is that possibly the leak?
Is segmentControl a property? Do you nil it in viewDidUnload?
No alloc is required when you create the array.
NSArray *myArray = [[NSArray alloc] initWithObjects: #"Add", #"Edit",
nil];
Use :
+ (id)arrayWithObjects:(id)firstObj, ...
Try it this way, this also doesn't needs release.