I came across this fragment of Objective-C:
NSNumber *theBalance =
[[[NSNumberFormatter alloc] init]
numberFromString: [textField text]];
This seems to leak the NSNumberFormatter. In C++ I would do one of two things:
use auto (i.e. stack) storage for the NSNumberFormatter
use RAII (e.g. shared_ptr) to manage the life of the NSNumberFormatter
In Objective-C neither of these options seem to be possible. I tried on the stack:
NSNumberFormatter fmt;
But this doesn't compile. As far as I can find there's no direct equivalent of RAII in Objective-C. I'm probably looking at the problem from the wrong angle as a mainly C++ programmer, so:
In the general case what's the correct, idiomatic (modern) Objective-C way of handling the life of objects like the NSNumberFormatter here? Do I really have to do it explicitly myself?
In the specific case is there a better way of solving the actual problem?
Most of the classes like NSString, NSArray, and so on, have the convenience constructors like, [NSString string] and [NSArray array] which return the autoreleased objects. NSNumberFormatter doesn't have any convenience constructors. So, you can send a autorelease message to let it autoreleased when the autorelease pool drains.
NSNumber *theBalance = [[[[NSNumberFormatter alloc] init] autorelease]
numberFromString: [textField text]];
If you want to retain(own) the reference of the object, you can omit the autorelease and release it later when you are done with it. You do it like this,
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
NSNumber *theBalance = [numberFormatter numberFromString: [textField text]];
// Later... somewhere in your code...
[numberFormatter release];
I know the above is not a detailed explanation. I'd suggest you to read this post by which, I hope, you would get some clear idea about memory management!
Related
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.
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.
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 .
SomeObject *temp = [[SomeObject alloc] init]
self.theObject = temp;
[temp release];
Why is it always done that way? Why not
self.theObject = [[SomeObject alloc] init];
If the theObject property is a retaining property, the first way is correct, because it doesn't leak memory. It's also more efficient than the correct way to write the second version, which is this:
self.theObject = [[[SomeObject alloc] init] autorelease];
Whenever you create an object with alloc you're in charge of releasing it somehow, whether by release or autorelease.
The second version leaks the SomeObject instance, since self.theObject will call a setter that, if properly written, retains the object.
You could just do
theObject = [[SomeObject alloc] init];
and some people certainly do. Others prefer to always use accessors though, either for consistence or to avoid bugs if the accessors have side effects (for exmaple, you would be bypassing KVO notification, which could be a problem if it's not part of an init method).
Instruments tells me the following line from the code below is leaking: I can't figure out how to fix this leak.
[self.selectedElement.usrAdvancedBuyingPercents replaceObjectAtIndex:selectedRow withObject:[numberFormatter stringFromNumber:percentage]];
- (IBAction) simpleMarginSliderValueChanged:(UISlider *)sender {
NSDecimalNumber *percentage = (NSDecimalNumber *)[NSDecimalNumber numberWithFloat:[sender value]];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setPositiveFormat:#"#.##"];
[self.selectedElement.usrAdvancedBuyingPercents replaceObjectAtIndex:selectedRow withObject:[numberFormatter stringFromNumber:percentage]];
[numberFormatter release];
}
The NSString you are creating from the number is not being released somewhere.
The problem is not in the code that is shown - it's somewhere else that is taking a string from that array, retaining it, then not releasing it. Leaks just shows you where memory that is leaked was initially allocated, and the only thing on that line that is allocating memory is [numberFormatter stringFromNumber:percentage].
Either that, or the whole array is not being released correctly (but then whatever builds usrAdvancedBuyingPercents would also show that is leaking).