Should I autorelease after failure in non-designated initalizer? - objective-c

Say I have the following class:
#interface Frob : NSObject {
NSData *data;
}
- (id)initWithData:(NSData *)inData;
#end
#implementation Frob
- (id)initWithData:(NSData *)inData
{
if ((self = [super init])) {
data = [inData retain];
}
return self;
}
- (void)dealloc
{
[data release];
[super dealloc];
}
#end
I now want to add a convenience method to read from a URL. So I write something that looks like this:
- (id)initWithContentsOfURL:(NSURL *)url
{
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:URL options:0 error:&error];
if (!data) {
NSLog(#"Frob couldn't read data, error: %#", error);
[self autorelease]; // !!!
return nil;
}
return [self initWithData:data];
}
That !!! line raised a red flag for me: I'm (effectively) invoking -release on an object that has been +alloc'd but the superclass's -init is never actually called. Is this safe?
Another way to ask the same question: Conceptually, is -release the opposite of +alloc? Or of +alloc and -[<root class> init]?
Clarification would be very much appreciated.

Another way to ask the same question: Conceptually, is -release the opposite of +alloc? Or of +alloc and -[ init]?
No it's not conceptually the opposite. However, conceptually, alloc does a retain which is the opposite of release and your release when initialisation fails undoes that retain.
Note that as a result of the release in your init, dealloc will be called, so dealloc needs to work with a partially (or not at all) initialised object. In your case, your instance variables will all be nil/NULL/0/false when dealloc is called.

That's perfectly ok, since there will be no handle to your object after you return nil. If anything you should do a release instead of an autorelease. You're mostly covered by the fact that [nil release] is a no-op, since the superclass hasn't been initialized, nothing in it will be non-nil.

Related

NSMutableString returning (null)

What's wrong with this? The NSMutableString returns (null).
.h:
NSMutableString *aMutableString;
...
#property (assign) NSMutableString *aMutableString;
.m:
#synthesize aMutableString;
- (void)aMethod {
[self setAMutableString:[[NSMutableString alloc] initWithString:#"message: "]];
if (someCondition) {
[[self aMutableString] appendString:#"woohoo"];
}
}
- (void)anotherMethod {
NSLog(#"%#", [self aMutableString]);
[[self aMutableString] release];
}
First of all, your code has a couple of problems. First, you should define your aMutableString #property as retain, not assign. assign is generally for primitive, non-object types, like ints, etc., and for some special cases of objects. You appear to want to take ownership of aMutableString in such a way that it persists after the event loop returns. In your posted code, you end up accomplishing that because of how you incorrectly set the aMutableString in the following line:
[self setAMutableString:[[NSMutableString alloc] initWithString:#"message: "]];
By creating an NSMutableString with alloc/init, you're creating a potential memory leak situation, though in your situation, it actually makes up for your defining the property as assign rather than retain.
Your second -anotherMethod is also potentially dangerous in that:
1) it releases an instance variable you defined as assign
2) after releasing it, it doesn't set it to nil. If you try to access that instance variable elsewhere in that class at a later point in time, you will likely get a crash because the pointer is no longer valid, if the instance variable has been dealloced.
So, the code should most likely look something like this:
.h
NSMutableString *aMutableString;
...
#property (retain) NSMutableString *aMutableString;
.m:
#synthesize aMutableString;
- (void)dealloc {
[aMutableString release];
[super dealloc];
}
- (void)aMethod {
[self setAMutableString:[NSMutableString stringWithString:#"message: "]];
if (someCondition) {
[aMutableString appendString:#"woohoo"];
}
}
- (void)anotherMethod {
NSLog(#"%#", aMutableString);
// the following is potentially unsafe!
// [[self aMutableString] release];
// it should be one of the following:
[aMutableString release]; aMutableString = nil;
// or
// [self setAMutableString:nil];
}
That said, without more information, it's a little hard to say what the problem is. I assume you mean the NSLog() call is printing (null)? If so, that means that aMutableString is still nil. Are you calling -aMethod before calling -anotherMethod?
If you want to make sure that aMutableString is initialized to an empty string, you could override -init:
- (id)init {
if ((self = [super init])) {
aMutableString = [[NSMutableString alloc] init];
}
return self;
}
Make sure you have #synthesize aMutableString; in your .m file

Memory Leak - Instruments, Objective-C

I have a UITextField and in the delegate class I have a UITableView. Here is the code:
- (BOOL) textField: (UITextField *)theTextField shouldChangeCharactersInRange: (NSRange)range replacementString: (NSString *)string {
value = [[theTextField.text stringByReplacingCharactersInRange:range withString:string] retain];
[valueTable reloadData];
return YES;
}
"value" is an NSString declared at the top of my class as "NSString *value;" and "valueTable" is just a UITableView. When I test for memory leaks, I am getting a "100%" memory leak on the line "value = [[theTextField.text stringByReplacing..." and I tried removing the "retain" on that line. However then later on when I called upon "value," it was nil, which is not good.
So how can I fix the memory leak? And what is the memory leak? Thanks!
The memory that is being leaked is the memory pointed to by value.
Every time your text field changes, the method stringByReplacingCharactersInRange... is returning an autoreleased NSString object. You are correct to retain it, so that it isn't deallocated. The problem, is that you now own memory somewhere. (You own that NSString by retaining it.)
The next time that method is called, when the user changes the text in that field, you're pointing value to a completely different memory location. The original memory that you had retained still exists, and will continue to persist forever. (Since you never released it.)
It's very important to match any retain method calls with an associated release. You could do either:
...
if (value) {
[value release];
}
value = ...;
...
OR
You can define the NSString *value as a property on your class, like:
#property (nonatomic, retain) NSString *value);
/* Implementation file */
#synthesize value;
Then simply use:
...
self.value = ...;
...
Also, since you're always going to have retained memory after that method has been called, you'll need to release value when your class is deallocated, as was mentioned in another answer:
- (void)dealloc {
// Only do *one* of the two following releases
// (1) If you're not using properties:
[value release];
// (2) If you are using properties:
self.value = nil;
[super dealloc];
}
Edit: It sounds like you should definitely read Apple's memory management guide before continuing:
http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/MemoryMgmt/MemoryMgmt.html
You should release value later on when you're done with it. For example, you can release it when in the delegate's dealloc method:
- (void)dealloc {
[value release];
// other memory management code...
[super dealloc];
}
see Apple's documentation on memory management.
As the others have mentioned, the problem is in the following line:
value = [[theTextField.text
stringByReplacingCharactersInRange:range withString:string] retain];
The issue with this code is that you are not releasing the old value of value before assigning the new value. Changing the code to the following should fix the leak, provided there are no other unforeseen problems elsewhere:
- (BOOL)textField:(UITextField *)theTextField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string {
NSString *newValue = [[theTextField.text
stringByReplacingCharactersInRange:range withString:string] retain];
[value release];
value = newValue;
[valueTable reloadData];
return YES;
}
- (void)dealloc {
[value release];
[super dealloc];
}

Allocation of instance variables?

Can anyone tell me if the NSString instance variable "planetName" needs to be allocated / released by me (as in the example below) or is that done when the class instance is created / allocated?
My understanding is that int and float don't need to be, but not sure about NSString & NSArray ...
#interface PlanetClass : NSObject {
NSString *planetName;
}
- (NSString *)planetName;
- (void)setPlanetName:(NSString *)value;
#end
Like this ...
- (id) init {
[super init];
planetName = [[NSString alloc] init];
return self;
}
- (void) dealloc {
[planetName release];
[super dealloc];
}
** ---------------------------------- **
EDIT: EDIT: Here is another version
** ---------------------------------- **
int main(int argc, const char *argv[]) {
// ** Allocated here
PlanetClass *newPlanet_01 = [[PlanetClass alloc] init];
NSString *newPlanetName = [NSString alloc] init];
// ** Set the instance variable pointer here
newPlanetName = #"Jupiter";
[newPlanet_01 setPlanetName:newPlanetName];
// ** Released here
[newPlanet_01 release];
return 0;
}
the init & dealloc methods would then be like this ...
- (id) init {
[super init];
return self;
}
- (void) dealloc {
// Always release the current copy of planetName
// pointed to by the class instance.
[planetName release]
[super dealloc];
}
The setPlanetName method would look like this ...
- (void)setPlanetName:(NSString *)newPlanetName {
if (planetName != newPlanetName) {
[planetName release];
planetName = [newPlanetName copy];
}
}
PS: I am not using properties or synthesize, I have not gotten that far yet.
cheers -gary-
Your code is valid, but there's probably no reason to initialize planetName to an empty string. One of the nice features of Objective-C is that you can send messages to a nil object with no consequence. If your class is initialized and you never call -setPlanetName:, planetName will be nil (instance variables are always initialized to nil), so when your -dealloc method calls [planetName release], nothing will happen.
In general, the best practice is to use -copy when setting an NSString instance variable, and -retain when setting most other objects as instance variables. As such, your -setPlanetName: method would look something like this:
- (void)setPlanetName:(NSString *)newPlanetName {
NSString *tempPlanetName = [newPlanetName copy];
[planetName release];
planetName = tempPlanetName;
}
You still have an issue with your new code.
In your main function, you release newPlanetName but this is a little wrong. Your PlanetClass retained it with its setPlanetName: method, but your PlanetClass never releases it again unless the name of the planet changes. It should not be up to the caller of setPlanetName: to keep hold of the string, it is your classes responsibility to deal with it appropriately.
Your old dealloc method is correct. It should release the planet's name because your PlanetClass no longer needs it. Your main method should not release the planet's name because the string returned by stringWithString: does not belong to you, and you give it to PlanetClass to take care of.
So, keep your old dealloc method, and remove the [newPlanetName release] from the main function and you should be alright from there.
As a shortcut, you can even call [newPlanet_01 setPlanetName:#"Jupiter"] and do away with the newPlanetName variable altogether in your main function.
planetName is a pointer which, like an int or float, does not need to be allocated or initialized.
Just like you can assign values to an int or float, you can point planetName at different instances of a string, or it can point at nothing.
When you init your class, planetName will be nil (not pointing at anything). If you point planetName to an instance of a string, you have to retain that string, and release in dealloc.
In other words, this:
planetName = [[NSString alloc] init];
is unnecessary and meaningless.
In your setPlanetName method you would need to release the existing string that planetName is pointing to, assign planetName to the new string, and then retain the new string.
Your dealloc method is correct.
Your code looks good. NSObject subclasses (NSString included) need to have their memory mananged by the object that owns them. In this case, that owner is PlanetClass.

Is releasing memory of Objective-c 2.0 properties required?

Something I have been wondering about properties for a while. When you are using properties, do you need to override the release message to ensure the properties are released properties?
i.e. is the following (fictitious) example sufficient?
#interface MyList : NSObject {
NSString* operation;
NSString* link;
}
#property (retain) NSString* operation;
#property (retain) NSString* link;
#end
#implementation MyList
#synthesize operation,link;
#end
You should always release the backing variables in dealloc:
- (void) dealloc {
[operation release];
[link release];
[super dealloc];
}
Another way:
- (void) dealloc {
self.operation = nil;
self.link = nil;
[super dealloc];
}
That's not the preferred way of releasing the objects, but in case you're using synthesized backing variables, it's the only way to do it.
NOTE: to make it clear why this works, let's look at the synthesized implementation of the setter for link property, and what happens when it is set to nil:
- (void) setLink:(MyClass *) value {
[value retain]; // calls [nil retain], which does nothing
[link release]; // releases the backing variable (ivar)
link = value; // sets the backing variable (ivar) to nil
}
So the net effect is that it will release the ivar.
In non-GC applications, yes. It is usual to assign nil instead of releasing the ivars.
My best experience is to release ivars initialized with init and assign nil to properties with retain and copy mode.
In your case I would assign nil
- (void) dealloc {
self.operation = nil;
self.link = nil;
[super dealloc];
}
The best way to do this is:
- (void)dealloc {
[operation release], operation = nil;
[link release], link = nil;
[super dealloc];
}
It would indeed be more convenient to use the generated setter methods
self.operation = nil;
but that is frowned upon. You don't always know which thread an object is deallocated on. Thus using an accessor may cause problems by triggering KVO notifications.
The catch here is that you need to adapt your dealloc to match the object management policy defined in your #property. E.g. don't go releasing a iVar backing an (assign) property.
No, you override the -dealloc method. And yes, if you don't release your properties (or rather, the backing ivars), you will leak. So in your #implementation here you should have something like
- (void)dealloc {
[operation release];
[link release];
[super dealloc];
}
Synthesizing a property only creates getter and setter methods, and therefor won't release the ivar when the object is deallocated. You need to release the ivar yourself.
In pre-ARC whenever you see new, alloc, retain and copy, whether it is an instance var or a property you must release. In ARC whenever you have a strong variable you must set it to nil.
In either case you have to override dealloc().

Problem with releasing an object

I've got this code:
Entry.h
#import <Foundation/Foundation.h>
#interface Entry : NSObject {
id object;
SEL function;
}
#property (retain) id object;
#property (assign) SEL function;
-(Entry*) initWithObject:(id)object selector:(SEL)function;
#end
Entry.m
#import "Entry.h"
#implementation Entry
#synthesize object;
#synthesize function;
-(Entry*) initWithObject:(id)obj selector:(SEL)sel {
self = [super init];
[self setObject:obj];
[self setFunction:sel];
return self;
}
-(void) dealloc {
[super dealloc];
if ([self object] != nil)
[[self object] release];
}
#end
And when I do this:
Entry *hej = [Entry alloc];
[hej release];
I get:
objc[2504]: FREED(id): message object sent to freed object=0xf5ecd0
Program received signal: “EXC_BAD_INSTRUCTION”.
What am I doing wrong?
(And this insert code thing at stack overflow doesnt work, unless I'm doing something wrong and you're not supposed to click "code sample" and then paste.)
+alloc only allocates memory. You need -init to actually create the object in that memory space. Since you are only allocating memory and not creating an object there, calling -release on a chunk of memory is giving you an error. Further, you want your [super dealloc] call to appear at the end of you -dealloc method. Change those two things and the following should work:
Entry *hej = [[Entry alloc] init];
[hej release];
there are two problems here:
1) you need to check that self = [super init] does not return nil. Typical usage would be to follow wrap your initialization code with the conditional:
if ((self = [super init]) != nil) {
// initialize the object...
}
2) but where you are getting stuck is on instantiating your object: you should do it like this:
Entry *hej = [[Entry alloc] initWithObject:myObj selector:mySelector];
(assuming that you want to go through the custom initializer you just defined...
else just use the default init method.) but 'alloc' must be followed by an init.
Entry *hej = [[Entry alloc] init]; // will do the trick...
Firstly, you need an init to go with your alloc. Second, in dealloc, you send a message to self after calling [super dealloc]. You can't do that. The final deallocation should go at the end.
I would also recommend changing:
if ([self object] != nil)
[[self object] release];
to:
[self setObject:nil];
It's less code and functionally equivalent. =)
There are many things wrong with your code. I'll try to go through them.
First, its better to use a different ivar name to your property name so its clear where you are using each. Apple normally uses an underscore prefix, but any prefix will do.
#interface Entry : NSObject {
id _object;
SEL _function;
}
#property (retain) id object;
#property (assign) SEL function;
#synthesize object = _object;
#synthesize function = _function;
Next, you aren't using the standard init template (although this probably wont make any difference normally).
-(Entry*) initWithObject:(id)obj selector:(SEL)sel {
self = [super init];
if (self != nil) {
// initializations
}
return self;
}
Next, Apple (for good reasons) recommends against using getters/setters in your init/dealloc. So your init would be:
-(Entry*) initWithObject:(id)obj selector:(SEL)sel {
self = [super init];
if (self != nil) {
_object = [obj retain];
_object = sel;
}
return self;
}
Next, after [super dealloc] your object is destroyed, so you cannot reference self (and hence your ivars) after that, so your dealloc should look like:
-(void) dealloc {
// your deallocations
[super dealloc];
}
Further, as above, Apple recommends you should not use setters or getters in your dealloc routine, so your deallocation would initially look like:
if (_object != nil)
[_object release];
But further still, Objective C allows (and Cocoa encourages) that sending a method to nil does nothing. This is in stark contast to most other languages where messaging nil would cause a crash, but it is how Objective C/Cocoa work and you need to get used to it. So your deallocation is actually just:
[_object release];
And finally, alloc only allocates the memory for your object, you have to initialize it, so the initialization would be something like:
Entry *hej = [[Entry alloc] initWithObject:myobj selector:#selector(mymethod)];