Understanding memory management in Objective-C - iphone

I've got the following code that works OK, but I'm not sure if I understood correctly some memory management concepts:
#import "mapPoint.h"
#implementation mapPoint
#synthesize coordinate, title, subtitle;
-(id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t {
[super init];
coordinate = c;
[self setTitle:t];
// Set date as subtitle
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
NSString *myDate = [dateFormatter stringFromDate:[NSDate date]];
[self setSubtitle:myDate];
[dateFormatter release];
// Look for city and state; when found, set it in subtitle, replacing date
geocoder = [[MKReverseGeocoder alloc] initWithCoordinate:c];
[geocoder setDelegate:self];
[geocoder start];
return self;
}
-(void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error {
NSLog(#"%#", error);
}
-(void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark {
[self setSubtitle:[NSString stringWithFormat:#"City: %#, State: %#", [placemark locality], [placemark administrativeArea]]];
}
-(void)dealloc {
[title release];
[subtitle release];
[geocoder release];
[super dealloc];
}
#end
As I created the geocoder object via the alloc method, I have to release it (done in dealloc). Correct?
In method reverseGeocoder:didFindPlacemark a NSString is created with the convenience method stringWithFormat. As I didn't use alloc, I'm not responsible for releasing that (I assume that method uses autorelease). Is that right?
The object placemark returns two NSStrings, locality and administrativeArea. Again, I didn't create those strings, so I'm not releasing them. However, given that they are part of the subtitle property of my mapPoint object, I don't want those strings to disappear, but they probably were created with autorelease. The property subtitle is declared with retain. Is is correct to assume that it will retain the NSString created with the convenience method, preventing it's premature destruction?
Thanks, and apologies if the questions are complicated... the subject is.

geocoder is an ivar in your class? Then your code is correct. Note that, since the geocoder is useless after it returns a result, you could release it (and set the ivar to nil) in the appropriate delegate methods to allow the memory to be reclaimed earlier.
Correct, mostly. If you had used a method starting with "new" or containing "copy" to get the instance of the object, you would also be responsible for releasing it.
Correct on all points. Note that if you were to implement setSubtitle: yourself instead of allowing #synthesize to do it, you would be responsible for implementing that behavior in your implementation of setSubtitle:.
Edit:
I see in comments that you are particularly concerned about point 3. The strings from [placemark locality] and [placemark administrativeArea] are passed as parameters to NSString's stringWithFormat method, after which they are irrelevant because their content has been copied into the new string that stringWithFormat returns. That is the only string you really have to worry about, and as has been pointed out setSubtitle: will retain that string.

Much has been written and said -- here and elsewhere -- about Cocoa memory management. Fundamentally, it's all an attempt to paraphrase or explain this: Memory Management Rules. Accept no substitutes. :-)

Yes, you need to release geocoder in dealloc
Yes, you are not responsible for releasing the NSString
Yes, you are not responsible for releasing those NSStrings.
To obtain a better understanding of the memory management rules and standards, I would suggest reading the 'Memory Management Programming Guide' In particular, you may want to take some extra time to read the section 'Object Ownership and Disposal'.

Others have already addressed your main questions, but I'd like to point out a few other things:
It is conventional to assign the result of [super init] to self, like this:
- init
{
self = [super init];
if (!self) return nil;
// do initialisation
return self;
}
There are other, similar constructs that revolve around the value returned by [super init], but they always (by convention) involve assigning to self first.
Although unlikely to cause any problems in this case, it is generally more acceptable to avoid using accessor methods like setTitle: and setSubtitle: inside an initialiser method. The reason being that you may inadvertently trigger KVO notifications when your object is only in a partially-initialised state.

Related

Memory Management Headache

I get leaks if I dont put it in dealloc. I get a crash EXC_BAD_ACCESS If I do. I cannot see anything wrong with this code. The bad access is pointed at [events release]. Have I made a mistake in the code below or is Instruments just having a laugh at my expense?
events is an NSArray
#interface EventsViewController : UITableViewController
{
#private
NSArray *events;
}
- (void)viewDidLoad
{
events = [[self getEvents] retain];
}
- (void)dealloc
{
[events release];
[super dealloc];
}
- (NSArray*)getEvents
{
NSMutableArray *response = [[[NSMutableArray alloc] init] autorelease];
//Some sql
while(sqlite3_step(statement) == SQLITE_ROW)
{
Event *event = [[[Event alloc] init] autorelease];
event.subject = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
[response addObject:event];
}
return response;
}
Update
A lot of you are saying the code is fine which is a plus. I dont manipulate events elsewhere - I have removed any code that does to try and single out the crash. Perhaps its in the parent view?
This is the click event that pushes the EventsViewController:
- (void)eventsClick:(id)sender
{
EventsViewController *eventsViewController = [[EventsViewController alloc] initWithNibName:#"EventsViewController" bundle:nil];
eventsViewController.anywhereConnection = anywhereConnection;
eventsViewController.contact = contact;
[[self navigationController] pushViewController:eventsViewController animated:YES];
[eventsViewController release];
}
The crash is actually happening when I return to the parent view. (I think it is considered a parent in this scenario). But perhaps the [eventsViewController release] just triggers dealloc in the EventViewController.
Have you considered just refactoring your code to use ARC? It works with iOS 4 and up and will make your life a lot easier. There are plenty of tutorials out there that will guide you how to do it, and will remove the need to manually figure out the nuances of memory management.
If your Events object has property 'subject' set as assign, then the results of stringWithUTF8String: will not be retained. (Same thing if Events is a C++ object.)
The stringWithUTF8String: method returns an auto-released object that will be released at the next turn of the event loop.
There is a huge difference when you reference a variable via "self", and when you don't.
When you use
events = [[self getEvents] retain];
the memory allocated in getEvents never gets stored in the class property and is basically a leak.
You need to use
self.events = [self getEvents]; // no need to use retain if property is correctly defined.
Then
[events release];
should work fine.
try putting
events = nil;
in dealloc.

Self with an array and addObject

When inserting an object into an array with a property is there any reason to invoke the getter/setter with self? i.e.
[self.myArray insertObject: myObject];
Or can I just use:
[myArray insertObject: myObject];
the gist would be:
.h
#interface ArrayViewController : UIViewController <UITextFieldDelegate>
{
NSMutableArray *myArray;
int itemNumber;
}
#property (nonatomic, retain) NSMutableArray *myArray;
#end
.m
- (IBAction)createMyArray
{
self.myArray = [[NSMutableArray alloc] initWithObjects: nil];
}
-(IBAction) addItemToMyArray
{
NSString *myString = [[NSString alloc] initWithFormat:#"item %d",itemNumber];
[myArray addObject: myString];
//[self.myArray addObject: myString]; //Or should I use self?
[myString release];
NSLog(#"myArray = %#", myArray);
itemNumber++;
}
//- (void)dealloc etc. not shown
Conceptually, it doesn't matter, so long as your getter method only returns the existing field value and doesn't, eg, do some "just in time" allocation or some such.
However, it's good practice to come up with a policy (personal or group) that you stick with, so that the caveats of that policy become second nature. Constantly switching styles results in sloppy, buggy code.
I tend to always use the self. for properties, just to remind myself that they are, in fact, properties, and to make it less likely that I'll accidentally set the value without using the property notation.
Either will work but you need to be aware of what you are doing. Using self. will invoke the setter/getter methods while the other will just access the variable directly. Using the variable directly, while perfectly valid, is discouraged outside of the initializer and dealloc method. The reason is you are losing out on the benefits of the property, especially setting using self. because it will properly assign/copy/retain the value for you correctly. Another reason not use property variables directly is because of atomicity but in your case you declared it as nonatomic.
Both of those are fine. It's mostly a stylistic choice. Using self.myArray will result in a call to the getter [self myArray].
When using alloc/init you should not set the returned value to a property, as these will retain twice:
self.myArray = [[NSMutableArray alloc] initWithObjects: nil];
use
myArray = [[NSMutableArray alloc] initWithObjects: nil];
or
self.myArray = [NSMutableArray array];
for the initialization.
The insert operations are equivalent though.
I typically skip the getter because I rarely find it valuable and it clutters up the readability of the code a bit. However, I tend to use the setter because I find it easier to allow the auto-generated setter methods to handle the retain/release semantics
In your case it's not an obligation to use self.myArray but for this case belloaw it will be an obligation:
-(void) addItemToMyArray:(NSAarray *)myArray
{
NSString *myString = [[NSString alloc] initWithFormat:#"item %d",itemNumber];
[self.myArray addObject: myString];
[myString release];
NSLog(#"myArray = %#", self.myArray);
itemNumber++;
}
to difference between the class attribut and the function argument.

ObjectiveC - Releasing objects added as parameters

Ok, here goes.
Being a Java developer I'm still struggling with the memory management in ObjectiveC. I have all the basics covered, but once in a while I encounter a challenge.
What I want to do is something which in Java would look like this:
MyObject myObject = new MyObject(new MyParameterObject());
The constructor of MyObject class takes a parameter of type MyParameterObject which I initiate on-the-fly.
In ObjectiveC I tried to do this using following code:
MyObject *myObject = [[MyObject alloc] init:[[MyParameterObject alloc] init]];
However, running the Build and Analyze tool this gives me a "Potential leak of an object" warning for the MyParameter object which indeed occurs when I test it using Instruments. I do understand why this happens since I am taking ownership of the object with the alloc method and not relinquishing it, I just don't know the correct way of doing it.
I tried using
MyObject *myObject = [[MyObject alloc] init:[[[MyParameterObject alloc] init] autorelease]];
but then the Analyze tool told me that "Object sent -autorelease too many times".
I could solve the issue by modifying the init method of MyParameterObject to say return [self autorelease]; in stead of just return self;. Analyze still warnes about a potential leak, but it doesn't actually occur. However I believe that this approach violates the convention for managing memory in ObjectiveC and I really want to do it the right way.
Thanx in advance.
Ok, here's a suggestion.
MyParameter *param = [[MyParam alloc] init];
MyObject *obj = [[MyObject alloc] initWithParam:param]; // do you know if param is retain'd or not?
[param release];
Why do it this way? This is the pattern used throughout ObjC/Cocoa. You add objects to NSArrays this way:
MyThing *thing = [[MyThing alloc] init];
[myMutableArray addObject: thing]; // this calls [thing retain]
[thing release];
You may also want to try to do this:
MyObject *obj = [[MyObject alloc] initWithParam: [MyParameter parameter]];
where:
+ (id) parameter
{
return [[[self alloc] init] autorelease];
}
that way you don't have to worry about it. This is the same as
[NSData data];
[NSArray array];
I hope that helps. Generally, it isn't a good idea to use init during another method call (like a setter or another init). Hiding it behind a Class Method (+ (id) parameter) means the user knows it'll be autorelease'd.
If you're ever unclear about how many retain's or release's something has, you can always do something like this:
[EDIT]
Caveat: Apparently you should never use -retainCount. I find it useful for teaching learners the basics of retain'd Memory Management, but the point is well taken.
Never Use it in actual code or for performance testing. This is only used to learn what retain does (and I believe it functions properly in this case). It will never give you an intelligent answer for an object which is autorelease'd or further retain'd by a NSArray or Other Foundation/AppKit/UIKit Classes
MyParameter *param = [[MyParam alloc] init];
NSLog(#"param retain count: %d",[param retainCount]); // should be 1
MyObject *obj = [[MyObject alloc] initWithParam:param];
NSLog(#"param retain count: %d",[param retainCount]); // should be 2, if MyObject retains it.
[param release];
NSLog(#"param retain count: %d",[param retainCount]); // should be 1
Also, when you dealloc MyObject, you'll need to release param if you retain'd it during initialization.
The following guide put out by Apple should help you to understand Objective-C Memory Management a little better.
MyThing *thing = [[MyThing alloc] init];
[otherThing methodWithAThing:thing];
[thing release];
or:
[otherThing methodWithAThing:[[[MyThing alloc] init] autorelease]];
or (if there is a "convenience constructor" on the class you're using):
[otherThing methodWithAThing:[MyThing thing]];
MyObject *myObject = [[MyObject alloc] init:[[[MyParameterObject alloc] init] autorelease]];
should be ok, if there is no release in the init (there should definitely not be a release in the init). I often make a class method which makes a autoreleased object. So the code would be:
// using it:
MyObject *myObject = [[MyObject alloc] init:[MyParameterObject defaultParameters];
// in the class implementation of MyParameterObject
+ (MyParameterObject*) defaultParameters{
MyParameterObject* out = [[MyParameterObject alloc] init];
// set some values
return [out autorelease];
}
[[MyObject alloc] init:[[[MyParameterObject alloc] init] autorelease]]
Without knowing what's going on in the init method, this seems fine.
NB, though, that it's more Objective-Cish to spell this "initWithParameterObject:". Though they gag people new to the language, Obj-C's descriptive method names are actually really helpful for code readability.

Memory Leaks - Objective-C

Can anyone see potential leaks in this code? I'm getting a "100%" leak according to Instruments on the line "NSString *ender = [temp stringFromDate:now];"
NSDateFormatter* temp = [[NSDateFormatter alloc] init];
[temp setDateFormat:#"yyyy-MM-dd"];
NSDate *now = [NSDate date];
NSString *ender = [temp stringFromDate:now];
DashboardViewController *controller = [[DashboardViewController alloc] init];
[controller initWithStartDate:ender andEndDate:ender];
[controller initAccount:account];
[self presentModalViewController:controller animated:NO];
[temp release];
Do you release controller after all that stuff?
This advice is unrelated to original question, but I think you should rename the initWithStartDate:andEndDate: and initAccount: methods since typically methods with "init" in the name return new instances.
Perhaps create your own -(id)initWithStartDate:endDate:account: and call the designated initializer from within.
Then you would create a new controller instance with
DashboardViewController *controller = [[DashboardViewController alloc] initWithStartDate:ender endDate:ender account:account];
Gonzalo
Since you pass your controller instance to the -presentModalViewController: method, that method will retain your controller. So you can safely release your controller, but you also should release your controller, since the memory management rules state that objects that you alloc+inited are owned by you and must be released.
On the other hand - just a small note - NSDateFormatter is a "heavy" object, cache the instance and reuse it, if it's possible. Probably this is also the reason why Apple deprecated this method. You might call -init on NSDateFormatter from iOS 2.0 till iOS 3.2, but it is deprecated after iOS 3.2 .

Memory Management in Objective C

I have a question about reference counting.
This is my constructor:
- (id)initWithId:(NSString *)graphId;
In another class I make an object in the following way:
GraphViewController *graph =
[[GraphViewController alloc] initWithId:[[NSString alloc] initWithFormat:#"%s", current_ID];
My question is: how do I correctly release the string object?
Is it correct to release the string passed as a parameter?
Any of these ways will work:
(1)
GraphViewController *graph = [[GraphViewController alloc] initWithId:
[[[NSString alloc] initWithFormat:#"%s", current_ID] autorelease]];
(2)
GraphViewController *graph = [[GraphViewController alloc] initWithId:
[NSString stringWithFormat:#"%s", current_ID]];
(3)
NSString *aString = [[NSString alloc] initWithFormat:#"%s", current_ID];
GraphViewController *graph = [[GraphViewController alloc] initWithId: aString];
[aString release];
And, of course, graph needs to be released or autoreleased somewhere.
Never pass ownership around. I.e., in your call to initWithId: you create a string. Make sure it's released in the exact same place. Do not hand the ownership over, burdening another function with the task of releasing what you created (or retained, ...).
If you're not calling initWithId: an unusually high number of times per second, do an autorelease. Even better, use a convenience function. Do not let people talk you into "avoiding the autorelease pool". Because a) it's not so easy to avoid and b) you have more important things to worry about.
In code:
NSString* graphID = [NSString stringWithFormat: #"%s", current_ID];
GraphViewController* graph = [[[GraphViewController alloc] initWithId: graphID] autorelease];
In the constructor, you will simply retain the ID and in the dealloc you will release it again. And please... use (private) #property declarations for this. You can then completely remove the ivar "graphID" from your public interface (if it's private, of course).
#interface GraphViewController ()
#property (copy, nonatomic) NSString* graphID; // copy instead of retain for potentially mutable objects
#end
Then, in initializer and dealloc, boilerplate stuff like:
#synthesize graphID;
- (id)initWithId:(NSString*) ID;
{
self = [super init];
self.graphID = ID;
return self;
}
- (void) dealloc
{
self.graphID = nil; // controversial, Apple policy is [graphID release]; graphID = nil;
[super dealloc];
}
Using this style will make you sleep better at night. Delighted users will raise statues in your name.