内存管理 - 何时发布?

时间:2011-09-03 10:50:52

标签: iphone objective-c memory-management

我已经在我自己学习Objective C一段时间了,但仍然没有完全掌握内存管理。我什么时候应该发布属性?

示例,我有一个类将处理2(register& updateParticulars)不同的URLRequest连接。当registerConnection结束时,将执行updateParticularsConnection。

@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方法期间?如果我做得不对,请建议。

5 个答案:

答案 0 :(得分:2)

你有点需要根据具体情况来看待它。一般的答案是“一旦你完成它”,除非它是一个微不足道的分配。对于简单的分配(例如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中直接处理您的实例的ivars - 使用访问器,它们将为您节省大量的麻烦。这些是您使用访问器编写程序所做的更改:

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

答案 1 :(得分:2)

我仅将@property用于可以从此类外部获取/设置的公共类变量。 对于您拥有的requestURL等私有变量,无需创建retain ed属性。

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

那么你的何时发布?

的问题

适用于班级dealloc方法中的@property 和私有变量:完成后使用

答案 2 :(得分:1)

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

例如,如果您发现在代码中的某个点之后不再需要属性,那么此时可能会将其取消,但是将该版本保留在dealloc方法中仍然是明智的。

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

这些不是硬/快规则,但它们可以帮助您摆脱困境。在修改程序的过程中,改变控制流程或其他一些并且导致错过“巧妙放置”的版本,这简直太容易了。通过观察范围规则,您很少遇到这样的问题。 (并记录范围规则的任何例外情况。)

答案 3 :(得分:0)

release上的备忘单:

  

如果您用于创建对象的方法包含单词newcopyalloc,则您赢了,并且必须释放一次你不再需要参考了。

答案 4 :(得分:0)

你是对的。

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

在Cocoa中如果向nil对象发送消息没有任何反应,那么如果你确定为每个副本/ alloc / retain调用了release,那么你应该将它设置为nil。

在iOS 5.0中你可以使用ARC完全不需要自己为cocoa做内存管理,你仍然需要为corefoundation和其他基于C的API创建/保留/发布。