内存管理 - 何时发布?

问题描述:

我一直在学习Objective C,已经有一段时间了,仍然没有完全掌握内存管理。我应该何时发布属性?内存管理 - 何时发布?

实施例,我有一个类将处理2(寄存器& updateParticulars)不同的URLRequest连接。 updateParticularsConnection将在registerConnection完成时执行。

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

处理委托回调

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

我的问题是我应该,只要我可以释放我的财产,这是我觉得我现在在做什么?或者仅在dealloc方法期间?如果我做得不对,请指教。

+0

[Objective-C内存管理的可能的重复:何时\ [释放\]?](http://*.com/questions/3012001/objective-c内存管理什么时候发布) –

你有必要把它在个案的基础上。一般的答案是“一旦你完成了它”,除非它是一个微不足道的分配。对于普通的分配(例如NSString * firstName),您可以等到dealloc或被替换(例如setFirstName:)。这仅仅简化了实现。

你的例子稍有不同。

// 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; 

// 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; 

重要:你直接处理您的实例的实例变量在OP - 使用访问器,他们会为你节省大量的头痛。这些都是你会做使用存取写程序的变化:

- (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 
    } 
} 
+0

你的意思是我应该使用'self.requestURL = [[NSMutableURLRequest alloc] init ...'而不是'requestURL = alloc init'?每当我访问属性,我应该经常访问访问器?任何不应使用访问器的特殊场合? –

+0

*你的意思是我应该使用self.requestURL = [[NSMutableURLRequest alloc] init ...而不是仅仅requestURL = alloc init?* - 是的。只需添加一个版本或autorelease。 *当我访问属性时,我应该经常访问访问者?* - 当你是新的,是的。随着时间的推移,您将了解到使用直接访问的理想情况。 *任何不应使用访问器的特殊场合* - 不要在部分构建的状态(init ...,dealloc)中使用它们。 – justin

+0

也是,你不应该使用'[self.requestURL release]',而是'self.requestURL = nil'。 – justin

release备忘单:

如果您使用的方法来创建一个对象包含文字newcopyalloc然后你赢得了,将不得不释放它,一旦你不需要更多的参考。

你是对的。

时(如果)你需要他们,只要你不需要他们了释放他们可以创建他们,这样你会节省内存。 为init中的所有属性和iVar分配内存可能会减慢实例化过程。 寻找对象的内存是Cocoa中最慢的任务之一,您必须尝试在CPU使用率和内存消耗之间取得完美平衡 - 您无需担心此级别的CPU使用率。

可可

如果您发送邮件至零对象没有任何反应,所以如果你确信你叫释放每拷贝/分配/保留,那么你应该将其设置为零。

中的iOS 5.0,你可以使用ARC可以完全消除需要做内存管理可可自己,你仍然需要,虽然做的建立/保持/对的CoreFoundation及其他基于C的API的释放。

+0

在ARC下,你需要管理所有C API的内存,所以CoreFoundation,CoreGraphics,GCD,ABAddressBook ......它使它更容易,但它不会让它消失。 – Abizern

我使用@property仅可获取/从这个类的外部设置的公共类变量。 对于像requestURL这样的私有变量,您不需要创建retain ed属性。

对于定义中声明为retain的每个变量,设置self.variable都会将保留计数增加1。必须牢记这一点,因为最常见的泄漏问题是将属性设置为已保留的值,如self.myString = [[NSString alloc] init]。这里,myString即使不期望,也会保留2。

那么你的问题什么时候发布?

对于@property在类的dealloc方法和私有变量:当你使用它完成。

您通常应该尝试在同一范围内保持alloc/retain和release。如果该值是一个属性,则它的范围对于对象实例是全局的,并且应该在dealloc方法中释放。如果该值是方法中的局部变量(并且随后不会被分配给更具全局作用域的变量),那么您显然必须在该方法中释放它,并且您应该尝试在相同的{}括号内你在哪里做了分配/保留。

如果你发现你在你的代码的某个点之后不再需要一个属性,那么你可以在那个时候删除它,但将版本留在dealloc方法中仍然是明智的。

对于具有委托的对象,它偶尔会在委托方法中执行对象的发布,但您应该始终将其记录在alloc位置,并且应该100%确定所有通过委托的“最终”路径做释放。

这些都不是硬性/快速规则,但它们会让你摆脱困境。在修改程序的过程中,改变控制流或其他一些因素,导致错误放置“巧妙放置”的版本非常容易。通过观察范围规则,您很少会遇到这样的问题。 (并记录范围规则的任何例外情况。)