Управление памятью - когда выпустить? - PullRequest
4 голосов
/ 03 сентября 2011

Я уже некоторое время изучаю Objective C самостоятельно и до сих пор не совсем освоил управление памятью.Когда я должен освободить свойства?

Например, у меня есть класс, который будет обрабатывать 2 (register & 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?Посоветуйте, если я не правильно делаю.

Ответы [ 5 ]

2 голосов
/ 03 сентября 2011

Я использую @property только для переменных общего класса, которые можно получить / установить вне этого класса. Для приватных переменных, таких как requestURL, которые у вас есть, нет необходимости создавать retain ed свойство.

Для каждой переменной, объявленной в определении как retain, установка self.variable увеличивает количество сохраняемых данных на единицу. Об этом следует помнить, так как наиболее распространенной проблемой утечки является установка для свойства уже сохраненного значения, например self.myString = [[NSString alloc] init]. Здесь myString будет иметь счет 2, даже если вы этого не ожидаете.

Итак, ваш вопрос когда выпустить?

Для @property: в методе dealloc класса и для частных переменных: всякий раз, когда вы используете его .

2 голосов
/ 03 сентября 2011

Вы вроде как должны принимать это в каждом конкретном случае.общий ответ «как только вы закончите с этим», если только это не тривиальное распределение.для тривиальных распределений (например, NSString * firstName), вы можете просто подождать, пока не освободится или не будет заменен (например, 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
    }
}
1 голос
/ 03 сентября 2011

Как правило, вы должны пытаться сохранить выделение / сохранение и разблокирование в одной и той же области. Если значение является свойством, его область действия является глобальной для экземпляра объекта, и его следует освободить в методе dealloc. Если значение является локальной переменной в методе (и впоследствии не назначается переменной более глобальной области видимости), то очевидно, что вы должны освободить его в этом методе, и вы должны попытаться выполнить освобождение в тех же скобках {}, что и где вы сделали выделение / сохранение.

Если вы, например, обнаружите, что вам больше не нужно свойство после некоторой точки в вашем коде, вы можете обнулить его в этот момент, но все же разумно оставить выпуск в методе dealloc.

Для объектов с делегатами иногда происходит освобождение объекта в методе делегата, но вы всегда должны документировать это в расположении alloc и должны быть на 100% уверены, что все "конечные" пути через делегат делают выпуск.

Это не жесткие / быстрые правила, но они избавят вас от неприятностей. В процессе модификации программы чрезвычайно легко изменить поток управления или что-то подобное и заставить пропустить «умно размещенную» версию. Соблюдая правила прицела, вы редко сталкиваетесь с такой проблемой. (И документируйте любые исключения из правил области.)

0 голосов
/ 03 сентября 2011

Вы правы.

Вы можете создавать их, когда (и если) они вам нужны, и выпускать их, как только они вам больше не нужны, таким образом вы сохраните память. Выделение памяти для всех свойств и iVars в init может замедлить процесс создания экземпляра. Поиск памяти для объекта - одна из самых медленных задач в Какао, вам нужно попытаться получить идеальный баланс между использованием процессора и потреблением памяти - вам не нужно будет беспокоиться об использовании процессора на этом уровне.

в Какао, если вы отправляете сообщения объектам nil, ничего не происходит, поэтому, если вы уверены, что вызывали release для каждого копирования / alloc / retain, вам следует установить для него значение nil.

в iOS 5.0 вы можете использовать ARC, который полностью устраняет необходимость самостоятельно управлять памятью для какао, однако вам все равно нужно будет создать / сохранить / выпустить для corefoundation и других API на основе C.

0 голосов
/ 03 сентября 2011

Ваш шпаргалка на release:

Если метод, который вы используете для создания объекта, содержит слова new, copy или alloc, тогда вы выиграли его, и вам придется выпустить его, если вам больше не нужна ссылка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...