Instruments says I have leak in this function. I am new to Obj-C so pardon for missing something obvious besides I am not sure if I am doing everything right here.
(void) selectList:sender {
NSMutableString *nibName = #"myController";
MyOwnController *study = [[MyOwnController alloc] initWithNibName:nibName bundle:nil];
study.title = #"Fundamentals";
study.listNameToLoad = #"funds";
[self.navigationController pushViewController:study animated:YES];
[nibName release];
[study.title release];
[study.listNameToLoad release];
[study release];
study = nil;
}
Related questions. Once you do pushViewController should you always do a release on the controller you just pushed on the stack ?
You should read this first.
In summary, you release only if you create or copy. You have released quite a few variables which you haven't created/copied and is not your responsibility to release. You should hence not release nibName, study.title & study.listNameToLoad.
Again, You should release the 'title' and 'listNameToLoad' properties within the controller's dealloc method.
As for your related question, you can release the controller only if you don't need the reference any more. If you do choose to keep the reference, you need to release the reference later when you don't need it any more.
Related
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 new to Objective-C and can't seem to get the memory management code right. I have the following code:
Media* myMedia = [self.myMediaManager getNextMedia];
self.navigationItem.title = [self.myMediaManager getCategory];
[self.btnImage setImage:myMedia.imageFile forState: UIControlStateNormal];
[self.lblImage setText:myMedia.imageLabel];
//[myMedia release];
My app crashes if I uncomment the above line. Do I need to do something special when I instantiate myMedia?
EDIT:
If myMediaManager is supposed to release it, when would it do that. Here is my code for getNextMedia:
- (Media*) getNextMedia {
DLog(#"Start");
Media* nextMedia = [[Media alloc] init];
[self setNextMediaIndex];
if (self.mediaIndex > -1)
{
nextMedia = [mediaArray objectAtIndex: self.mediaIndex];
}
return nextMedia;
}
EDIT2: I fixed the crashing issue (I was releasing an object I didn't own). I still see leaks and can't seem to find what the issue is.
Only objects that you own can be released.
You can release objects if you new, alloc, copy, mutableCopy or retain them first.
Since there is no alloc/copy/retain in [self.myMediaManager getNextMedia]; you can't release it.
Since myMedia is not retained here, you don't need to release it. When the origin (self.myMediaManager) releases it, it gets destroyed immediately.
NSString *string = [[NSString alloc] init];
[string release]; // now we have to release the string, since we allocated it.
NSString *string = self.navigationItem.title;
// now we don't, since it's a property of `navigationItem` and we didn't retain it.
At this point, since you are just learning, you should probably just start off using ARC with the iOS5 beta versions of XCode. It's good to have an understanding but using ARC will save you many potential pitfalls - by the time you learn enough to produce something iOS5 will be out. You can still build applications targeting iOS4, so you'll still be able to reach a lot of people.
The general rule for memory management is as follows:
For every retain, alloc, copy, or new, you need to call release or autorelease.
Since you did not call any these, you do not need to release myMedia.
For more information, take a look at this other answer I posted that deals with the subject. Also, since you are new to iOS development, I suggest looking at this answer as well.
This updated code is suspicious:
Media* nextMedia = [[Media alloc] init];
[self setNextMediaIndex];
if (self.mediaIndex > -1)
{
nextMedia = [mediaArray objectAtIndex: self.mediaIndex];
}
Depending on the condition in the if() clause, you assign a new value to nextMedia, which makes the value you just allocated unreachable, i.e. it can't be released.
Also, you don't retain the value you get from the array, so you should not release it either. But if the if() clause does not run, you still have the instance you alloc-ed, and that should be released.
That is not good. Try:
Media* nextMedia = [[Media alloc] init];
[self setNextMediaIndex];
if (self.mediaIndex > -1)
{
[nextMedia release];
nextMedia = [[mediaArray objectAtIndex: self.mediaIndex] retain];
}
You could also do (and I would prefer that):
Media *nextMedia;
[self setNextMediaIndex];
if (self.mediaIndex > -1)
{
nextMedia = [[mediaArray objectAtIndex: self.mediaIndex] retain];
}
else
{
nextMedia = [[Media alloc] init];
}
Now you can release nextMedia when that is not needed anymore, without any ambiguity about the retain count.
Interface
#property (nonatomic, retain) PracticalSignsMainViewController *practicalVC;
Implementation
if (self.practicalVC == nil) {
PracticalSignsMainViewController *vc = [[PracticalSignsMainViewController alloc] init];
self.practicalVC = vc;
[vc release];
}
I only want to make one viewcontroller object in this case and change the data that it acts upon. However is it correct to retain or assign this viewcontroller (and why so)? It is used in a navigation-hierachy.
Thanks.
Retain. Typically you want object properties to be retained, and primitive properties to be assigned. The answer here is really good: Use of properties for primitive types
If you're planning to cache a view controller that's used in a navigation, you would need to retain it. The reason is that while the navigation controller retains it temporarily, once the user hits the back button the navigation controller will send the view controller a release message. If you haven't retained the view controller anywhere else at that point it will be deallocated.
If you're creating a new instance each time, that would be fine, but obviously that would destroy a cached instance if the property uses assign semantics.
Firstly, your code is right, but it can be more simple.
If you did the following synthesize,
#synthesize practicalVC; // synthesize release the existing reference if has
the following code is same as your code.
self.practicalVC = [[[PracticalSignsMainViewController alloc] init] autorelease];
As you mentioned, if your app does not need many access to the view controller, you need not to have view controller's instance separately.
============== Modified ====================
And I modified my answer after seeing answers of #6NSString #Abizern.
Regarding autorelease and coding style.
1. autorelease,
#6NSString said that "it uses autorelease which many avoid unless returning an newly alloced object."
But even if you use "autorelease" in return statement, the retain count of the returned object is not immediately decreased. It will be decreased at an appropriate time.
So If we want to decrease the retaincount explicitly and immediately, we should use paired "release".
-(NSObject*)testAuto {
return [[[NSObject alloc] init] autorelease];
}
...
self.anObj = [self testAuto];
// the retainCount of anObj is 2
..
-(void)testAuto {
self.anObj = [[[NSObject alloc] init] autorelease];
}
...
[self testAuto];
// the retainCount of anObj is also 2
..
-(void)testAuto {
self.anObj = [[[NSObject alloc] init] autorelease];
[self.anObj release]; // Yes, yes, you may say this code does not look good. :)
}
...
[self testAuto]
// the retainCount of anObj is 1
...
As you see, (I naturally tested above code again) "autorelease"s both in return statement and in alloc statement are almost same. And if you want to manage retain count harder, we should use "release".
I think "autorelease" in alloc statement is better than one in return statement because "autorelease" in return can be omitted. :)
2. Coding style,
As the end of "1. autorelease", I think, autorelease in alloc statement would be more safe to avoid missing release.
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.
I'm following a book on iPhone development and there is a particular pattern I keep seeing in the example code that doesn't make much sense to me. Whenever a property is set they first assign a pointer to the new value for the property, then set the property to the pointer, then release the pointer. Example:
Interface:
#interface DoubleComponentPickerViewController : UIViewController {
NSArray *breadTypes;
}
#property(nonatomic, retain) NSArray *breadTypes;
#end
Class method:
- (void)viewDidLoad {
NSArray *breadArray = [[NSArray alloc] initWithObjects:#"White", #"Whole Wheat", #"Rye", #"Sourdough", #"Seven Grain", nil];
self.breadTypes = breadArray;
[breadArray release];
}
Is there any reason to do this instead of just doing the following?
- (void)viewDidLoad {
self.breadTypes = [[NSArray alloc] initWithObjects:#"White", #"Whole Wheat", #"Rye", #"Sourdough", #"Seven Grain", nil];
}
Thanks for the light that will no doubt be shed :)
Let me try and explain it in a different way.
A method that has alloc, copy or new in its name will allocate memory for an object, and gives ownership of that object to the caller, and it is the caller's responsibility to release that memory.
In your viewDidLoad method, you call a method that gives you ownership of an object. It is your method's responsibility to release it. However, before you do that, you want to do something with it - after all, that's why you alloc'ed it, not to just release it, but to do something useful with it.
Regardless of what it is that you want to do with it, you have to release it (or autorelease it*). In this case your use of the object is to pass it to self.breadTypes. self.breadTypes may not look like a method, but it is (it is a setter). You pass it breadArray. It does what it needs to with it. It might retain it for use later, or it might copy some info out of it, or make a copy of the entire thing. Your viewDidLoad doesn't really care. It assumes that self.breadTypes does what it needs to and when it returns, it doesn't care what you do with breadArray.
And what you do with it, is what you have to do with anything that you own - release (or autorelease* it).
That's why you have to use the temp variable, breadArray. You can't quite release the results from alloc on the same line, since the object would get released before self.breadTypes can have at it:
self.breadTypes = [[[NSArray alloc] initWithObjects:#"White", ..., nil] release];
Thus you are forced to assign to a temp variable, pass it to self.breadTypes, and then release the object that is saved in breadArray.
Now, you could try to do it this way so you don't use a temp variable:
- (void)viewDidLoad {
self.breadTypes = [[NSArray alloc] initWithObjects:#"White", #..., nil];
[self.breadTypes release];
}
but that is not very efficient since you are calling yet another method (self.breadTypes as a getter) that you didn't really need to if you have just stored the value in a temp variable.
*Now, as a responder said, you could use autorelease for an alternative version:
- (void)viewDidLoad {
self.breadTypes = [[[NSArray alloc] initWithObjects:#"White", ..., nil]
autorelease];
}
Apple urges us to think twice about whether we want to use autorelease vs. release. Autorelease may not be the best choice for every situation. I personally like to clean up after myself as soon as I possibly can, and not use autorelease needlessly. Autoreleased objects get released at the end of the execution of the run loop, for example soon after viewDidLoad returns. You should read up a bit more about autorelease (and memory management on the iPhone which is slightly different than MacOS X Cocoa), as I am oversimplifying it all.
BTW: If you retain an object, you are assuming ownership of it, and you will have the same responsibility again, to release it after you are done with it.
Yes. Those methods are alloc'ing the variables so they must be released. The fact that the property has a retain attribute means that when you say #synthesize breadTypes; the compiler is actually generating a setBreadTypes that properly releases the current breadType member and retains the new one. Thus your function must not retain the variable it alloc'ed.
You could, however write:
- (void)viewDidLoad {
self.breadTypes = [[[NSArray alloc] initWithObjects:#"White",
#"Whole Wheat", #"Rye", #"Sourdough",
#"Seven Grain", nil]
autorelease];
}
You'll want to brush up on Cocoa Memory Management