Memory Management - When to Release?

Iโ€™ve been studying Objective-C myself for some time now and still donโ€™t quite understand how memory management works. When should properties be released?

For example, I have a class that will handle 2 (register and updateParticulars) different URLRequest connections. updateParticularsConnection will be executed when registration is complete.

@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]; } 

Delegate Callback Handling

 - (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 release my properties as soon as I can, what I think I'm doing now? Or just during the dealloc method? Advise if I do not do it right.

+4
source share
5 answers

You need to take it on an individual basis. the general answer is โ€œas soon as you are done with it,โ€ if this is not a trivial distribution. for trivial distributions (e.g. NSString * firstName ) you can just wait until dealloc or it is replaced (e.g. setFirstName: . it just simplifies the implementation.

your example is a little different.

 // typically, you will set these to nil right when they // have finished and you have grabbed what you need. // that 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 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 process your ivars instance directly in OP - use accessors, they will save you a lot of headaches. these are the changes you need to make in order to write your program using accessories:

 - (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 } } 
+2
source

I use @property only for public class variables that can be obtained / set from outside this class. For private variables, such as requestURL , that you have, there is no need to create a retain ed property.

For each variable declared as retain in the definition, setting self.variable increases the number of deductions by one. This should be kept in mind, since the most common leakage problem is setting the property of a value that has already been saved, for example self.myString = [[NSString alloc] init] . Here myString will have a score of 2, even if you do not expect this.

So your question is when to release?

For @property : in the class dealloc method and for private variables: when you are done using .

+2
source

Usually you try to keep the selection / save and release in the same area. If the value is a property, then the scope is global for the object instance and must be released in the dealloc method. If the value is a local variable in the method (and subsequently is not assigned to a variable of a more global scope), then obviously you should free it in this method, and you should try to release in the same {} brackets as where you selected / saved .

If, for example, you find that you no longer need a property after some point in your code, you can use it at that point, but it is still wise to leave the release in the dealloc method.

For objects with delegates, it sometimes works to make the object free in the delegate method, but you should always document this in the placement and be 100% sure that all the "final" paths are through the delegate release.

These are not hard / fast rules, but they will save you trouble. It is terribly easy, in the process of modifying a program, to change the control flow or some of them and cause the omission of "smart placement". Observing the rules of the area, you rarely have such a problem. (And document any exceptions to area rules.)

+1
source

Your cheat on release :

If the method you use to create the object contains the words new , copy or alloc , then you won it and it will have to free it as soon as you donโ€™t need the link anymore.

0
source

You're right.

You can create them when (and if) you need them, and release them as soon as you no longer need them, thus you will save memory. allocating memory for all properties and iVars in init can slow down the instantiation process. Finding memory for an object is one of the slowest tasks in Cocoa, you should try to achieve the perfect balance between CPU usage and memory consumption. You will not need to worry about CPU consumption at this level.

in Cocoa, if you send messages to Nile objects, nothing happens, so if you are sure that you called the release for each copy of / alloc / preserve, then you should set it to zero.

in iOS 5.0 you can use ARC, which completely eliminates the need for memory management for Cocoa yourself, you still need to create / save / release for corefoundation and other C-based APIs

0
source

All Articles