Gavin
Gavin

Reputation: 2834

Memory Management - When to release?

I have been learning Objective C on my own for some time already and still don't quite get the hang of memory management. When should I release properties?

Example, I have a class that will handle 2 (register & updateParticulars) different URLRequest connections. updateParticularsConnection will be performed when registerConnection finishes.

@interface ConnectionViewController : UIViewController {

}
@property (nonatomic, retain) NSURLConnection *registerConnection;
@property (nonatomic, retain) NSURLConnection *updateParticularsConnection;
@property (nonatomic, retain) NSMutableData *responseData;
@property (nonatomic, retain) NSMutableURLRequest *requestURL;

@end

@implementation ConnectionViewController
@synthesize registerConnection, updateParticularsConnection, responseData, requestURL,


(void)performRegistration {
    // other here to prepare the data.
    requestURL = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"myURL"]];
    registerConnection = [[NSURLConnection alloc] initWithRequest:requestURL delegate:self startImmediately:YES];
}

(void)updateParticulars {
    // other here to prepare the data.
     [requestURL setURL:[NSURL URLWithString:@"http:myURL.com"]];
     updateParticularsConnection = [[NSURLConnection alloc] initWithRequest:requestURL delegate:self startImmediately:YES];
}

Handling delegate callbacks

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [SVProgressHUD dismissWithError:@"Unable to connect"];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    if (responseData == nil) {
        responseData = [[NSMutableData alloc] init];
    }
    [responseData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    if (connection == registerConnection) {
        NSMutableString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
        NSLog(@"Register connection recieved data reads : %@", responseString);
        if ([responseString isEqualToString:@"-1"]) { // error. stop connection
            [self.requestURL release]; // remember to release requestURL since we would not be continuing on.
        }   
        else if ([responseString isEqualToString:@""]) { // error. stop connection

            [self.requestURL release]; //remember to release requestURL since we would not be continuing on.
        }

        else {
            [self updateParticulars]; // perform next connection, updateParticulars

        }       
        responseData = nil; // clear the current stored data in preparation for the next connection.
        [self.registerConnection release];
        [responseString release];
    } // end of definition for register connection

    else if (connection == updateParticularsConnection) {
            // do stuff with data received back here 
        self.responseData = nil;
        [self.requestURL release];
        [self.updateParticularsConnection release];
    }       
}

My question is should I be releasing my properties as soon as I can, which is what I think I am doing now? Or only during the dealloc method? Do advise if I am not doing it right.

Upvotes: 4

Views: 294

Answers (5)

Hot Licks
Hot Licks

Reputation: 47759

You should generally attempt to keep the alloc/retain and release in the same scope. If the value is a property, it's scope is global to the object instance, and it should be released in the dealloc method. If the value is a local variable in a method (and isn't subsequently assigned to a variable of more global scope) then obviously you must release it in that method, and you should attempt to do the release within the same {} brackets as where you did the alloc/retain.

If you, eg, find that you no longer need a property after some point in your code you may nil it at that point, but it's still wise to leave the release in the dealloc method.

For objects with delegates, it occasionally works to do the release of the object in the delegate method, but you should always document this at the alloc location, and should be 100% sure that all "final" paths through the delegate do the release.

These aren't hard/fast rules, but they will keep you out of trouble. It's appallingly easy, in the course of modifying a program, to change control flow or some such and cause a "cleverly placed" release to be missed. By observing scope rules you rarely have such a problem. (And document any exceptions to the scope rules.)

Upvotes: 1

justin
justin

Reputation: 104708

you sort of need to take it on a case by case basis. the general answer is "as soon as you are done with it", unless it is a trivial allocation. for trivial allocations (e.g. NSString * firstName), you can just wait until dealloc or it is replaced (e.g. setFirstName:). this merely simplifies the implementation.

your example is slightly different.

// typically, you will set these to nil right when they 
// have finished and you have grabbed what you need.
// that's pretty common for an async one shot request.
@property (nonatomic, retain) NSURLConnection *registerConnection;
@property (nonatomic, retain) NSURLConnection *updateParticularsConnection;
@property (nonatomic, retain) NSMutableURLRequest *requestURL;

and

// in the context of your program, this allocation could be large.
// in many cases, you'll simply convert it to the destination (if
// it's an image, just turn it into an image without holding onto
// the data representation), then dispose of it.
@property (nonatomic, retain) NSMutableData *responseData;

important: you are handling your instance's ivars directly in the OP - use the accessors, they will save you a ton of headaches. these are the changes you'd make to write your program using the accessors:

- (void)performRegistration {
    self.requestURL = [[[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"myURL"]] autorelease]; // use setter and autorelease
    self.registerConnection = [[[NSURLConnection alloc] initWithRequest:requestURL delegate:self startImmediately:YES] autorelease]; // use setter and autorelease
}

- (void)updateParticulars {
    [self.requestURL setURL:[NSURL URLWithString:@"http:myURL.com"]]; // use getter
    self.updateParticularsConnection = [[[NSURLConnection alloc] initWithRequest:requestURL delegate:self startImmediately:YES] autorelease]; // use setter and autorelease
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    if (self.responseData == nil) { // use getter
        self.responseData = [NSMutableData data]; // use setter and autorelease
    }
    [self.responseData appendData:data]; // use getter
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    if (connection == self.registerConnection) { // use getter
        NSMutableString *responseString = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; // use getter
        NSLog(@"Register connection recieved data reads : %@", self.responseString); // use getter
        if ([responseString isEqualToString:@"-1"]) {
            self.requestURL = nil; // use setter
        }
        else if ([responseString isEqualToString:@""]) {
            self.requestURL = nil; // use setter
        }
        else {
            [self updateParticulars];
        }
        self.responseData = nil; // use setter
        self.registerConnection = nil; // use setter
        [responseString release];
    }
    else if (connection == self.updateParticularsConnection) { // use getter
        self.responseData = nil; // use setter
        self.requestURL = nil; // use setter
        self.updateParticularsConnection = nil; // use setter
    }
}

Upvotes: 2

Mridul Kashatria
Mridul Kashatria

Reputation: 4197

I use the @property only for public class variables which can be get/set from outside this class. For private variables like the requestURL that you have, no need to create a retained property.

For every variable declared as retain in the definition, setting self.variable increases the retain count by one. This has to be kept in mind as most common leakage issue is setting a property to already retained value, like self.myString = [[NSString alloc] init]. Here, myString will have retain count of 2 even when you don't expect it.

So your question of when to release?

For @property: in the class's dealloc method and for private variables: whenever you are done using it.

Upvotes: 2

Sepehr Mahmoudian
Sepehr Mahmoudian

Reputation: 771

You are right.

You can create them when (and if) you need them and release them as soon as you do not need them anymore, this way you will conserve memory. allocating memory for all properties and iVars in init can slow the instantiation process. Finding memory for an object is one of the slowest tasks in Cocoa, you have to try to get the perfect balance between CPU usage and Memory consumption–You won't need to worry about CPU usage at this level.

in Cocoa if you send messages to nil objects nothing happens, so if you are sure that you called release for every copy/alloc/retain, then you should set it to nil.

in iOS 5.0 you can use ARC which completely removes the need to do memory management for cocoa yourself, you will still need to do create/retain/release for corefoundation and other C based APIs though.

Upvotes: 0

Mundi
Mundi

Reputation: 80271

Your cheat sheet on release:

If the method you use to create an object contains the words new, copy or alloc then you won it and will have to release it once you do not need a reference any more.

Upvotes: 0

Related Questions