путаница с выпуском / авто-выпуском в какао для iphone - PullRequest
5 голосов
/ 13 июля 2009

Я медленно учу себя какао для iPhone (через Stanford Class на iTunes U ), и я только что прошел часть по управлению памятью, и я хотел, надеюсь, получить некоторое подтверждение того, что мои предположения о том, как обрабатывается память и как работают [release] и [autorelease]. Поскольку управление памятью является действительно базовой и фундаментальной, но очень важной частью опыта программирования, я хотел бы убедиться, что я все делаю правильно.

Я понимаю, что что-либо с alloc, new или copy должно быть выпущено.
Если я сделаю это:

NSString *temp = [[NSString alloc] initWithString:@"Hello World"];

Затем мне нужно со временем добавить [temp release / autorelease], так как у меня есть alloc.

Однако, если я сделаю это:

NSString *temp = @"Hello World";

Тогда, похоже, заявление о выпуске не требуется. Класс NSString вызывает автоматический выпуск как часть назначения?

Кроме того, есть ли разница между двумя * временными объектами здесь после этих утверждений? Они оба содержат одну и ту же строку, но существуют ли способы использования памяти / где они различаются?

Во-вторых, со свойствами я предполагаю, что авто-релиз обрабатывается автоматически. Если у меня есть это:

@interface Person : NSObject
{
    //ivars
    NSString *firstName;
    NSString *lastName;
}

//properties
@property NSString *firstName;
@property NSString *lastName;

///next file

@implementation Person

@synthesize firstName;
@synthesize lastName;

- (void) dealloc
{

    //HERE!!!!

    [super dealloc];
}

Я предполагаю, что мне не нужно добавлять [firstName release] и [lastName release] (на // ЗДЕСЬ !!!!), так как это автоматически обрабатывается свойствами. Это правильно?

Я понимаю, что если я делаю это в коде (при условии, что я определил initWithFirstName):

Person *Me = [[Person alloc] initWithFirstName: @"Drew", lastName:"McGhie"];

что позже мне придется использовать [Me release / autorelease];

Любая помощь, подтверждающая или исправляющая мое понимание, до сих пор высоко ценится.

ПИСЬМО ОТВЕТА НА ПОСТ

Я думал, что напишу все это после того, как рассмотрю все ответы, опробую предложения и расскажу о том, что сработало.

Мне нужно добавить [firstName release], [lastName release], но мне также нужно добавить (сохранить) в описания свойств. Не добавлять (сохранять) вызванные предупреждения, потому что это предполагает (назначить). Вот как я наконец настроил класс

@interface Person : NSObject
    {
        //ivars
        NSString *firstName;
        NSString *lastName;
    }

    //properties
    @property (retain) NSString *firstName;
    @property (retain) NSString *lastName;

    ///next file

    @implementation Person

    @synthesize firstName;
    @synthesize lastName;

    - (void) dealloc
    {
        [firstName release];
        [lastName release];
        [super dealloc];
    }

Ответы [ 6 ]

10 голосов
/ 13 июля 2009

Правило простое: если вы alloc, copy или retain, вы несете ответственность за release. Если вы этого не сделали, это не так. Однако, если вам нужно полагаться на объект, находящийся рядом, вам нужно retain (а затем release).

Мы можем обращаться со строковым литералом в соответствии с правилами - вам не нужно release, потому что он вам не принадлежит. Это просто; не нужно беспокоиться о том, являются ли они особыми случаями или нет, просто следуйте правилам, и все будет в порядке.

Я написал сообщение в блоге с подборкой статей о правилах управления памятью Какао ; Я бы порекомендовал следить за некоторыми ссылками.

3 голосов
/ 13 июля 2009
  1. Я никогда не выпускал строковые константы, такие как NSString *foo = @"x";. Логически, если бы вам пришлось release результат этого, то вам бы пришлось release параметр до initWithString и оба параметра до initWithFirstName:lastName: тоже.
  2. Вы должны сделать release или autorelease firstName и lastName. Я видел предупреждения о неиспользовании синтаксиса свойств в деструкторах, что, по-моему, является той же причиной, по которой вы не используете виртуальные функции в конструкторах и деструкторах C ++.

Ваше предположение было неверным. Вы должны сделать либо это:

    Person *Me = [[Person alloc] initWithFirstName: @"Drew"
                                          lastName: @"McGhie"];
    ...
    [Me release];

или это:

    Person *Me = [Person personWithFirstName: @"Drew"
                                    lastName: @"McGhie"];

... и убедитесь, что объект Person обрабатывает +personWithFirstName:lastName: правильно, т.е. [[[self alloc] initWithFirstName: firstName lastName: lastName] autorelease].

Вы, вероятно, должны сделать тот, у которого меньше кода. Ясность важна, NSAutoreleasePool, вероятно, никогда не будет вашим узким местом, и если это когда-либо будет легко исправлено.

Я думаю, что люди приложили много усилий, чтобы избежать сообщений класса, которые возвращают объект autorelease, который просто не заслуживает. Это преждевременная оптимизация, так как она, вероятно, не является необходимой и даже может быть неправильной. И его сложнее поддерживать, вы, скорее всего, будете преследовать утечки вечно.

Кроме того, вы собираетесь autorelease объект, который вам нужно было init (т.е. alloc + initPersonWithFirstName:lastName: вместо использования сообщения класса, такого как personWithFirstName:lastName:), я бы посоветовал вам сделать это немедленно , В противном случае вы, возможно, гонитесь за той же утечкой. Так что, если вы не собираетесь добавлять personWithFirstName:lastName: метод к Person, сделайте это вместо:

    Person *Me = [[[Person alloc] initWithFirstName: @"Drew"
                                           lastName: @"McGhie"] autorelease];

Резюме: Какао делает много, чтобы помочь вам с управлением памятью. Убедитесь, что вы не боретесь с этим.

Обновлено на основе отзывов Джона в комментарии.

2 голосов
/ 14 июля 2009

NSStrings, созданные с синтаксисом @"String here", являются константными строками. Они отличаются от обычных строк. Как и обычные строки констант Си, они создаются, когда ваша программа загружается и существуют в течение всего срока ее службы. Класс NXConstantString, к которому они принадлежат, игнорирует все сообщения управления памятью. Вы можете retain и release все, что вам нравится, и это не будет иметь никакого значения.

Для строки, созданной методом [[NSString alloc] initWith...], применяются обычные правила управления памятью . Я настоятельно рекомендую прочитать связанные документы - они не сложны, и после прочтения вы узнаете почти все, что вам когда-либо понадобится об управлении памятью Какао.

1 голос
/ 14 июля 2009

Класс NSString автоматически вызывает автоматический выпуск как часть назначения?

Класс NSString ничего не сделал, потому что вы не отправили ему сообщение. Все, что вы сделали, это присвоили переменную. NSString не узнает об этом, и это не его дело.

Кроме того, есть ли разница между двумя * временными объектами здесь после этих утверждений? Они оба содержат одну и ту же строку, но существуют ли способы использования памяти / где они отличаются?

Они оба строки NSS, они оба содержат одно и то же значение, и они оба считаются неизменяемыми. Это все, что тебя должно волновать.

Во-вторых, со свойствами я предполагаю, что авто-релиз обрабатывается автоматически. Если у меня есть это:

@property NSString *firstName;
@property NSString *lastName;

- (void) dealloc
{
    //HERE!!!!

    [super dealloc];
}

Я предполагаю, что мне не нужно добавлять [firstName release] и [lastName release]//HERE!!!!), так как это автоматически обрабатывается свойствами. Это правильно?

Нет. NSObject не выпустит все ваши значения имущества для вас; вам все еще нужно освободить их там.

Кроме того, не делайте self.firstName = nil и self.lastName = nil в dealloc. Они преобразуются в сообщения (для ваших методов доступа), и когда вы делаете это в dealloc, вы отправляете сообщения в объект с полуколичеством dealloc ked. Это напрашивается на неприятности. То же самое применимо и к другому способу инициализации значений свойств в init: при использовании ваших свойств / аксессоров можно было бы отправлять сообщения объекту наполовину init.

1 голос
/ 14 июля 2009

О firstName / lastName.

Для ясности всегда следует указывать атрибуты свойств. Атрибут сеттера по умолчанию назначить , в этом случае вы хотите использовать сохранить .

@interface Person : NSObject
{
    NSString *firstName;
}
@property (retain) NSString *firstName;
@end

При сохранении каждый раз вы используете точечную запись, чтобы присвоить значение, которое компилятор вставляет в сохранение: просто не забывайте всегда использовать его. Для согласованности советую написать инициализатор так:

- (id) initWithFirstName:(NSString*) aString
{
    self.firstName = aString; 
}

и метод dealloc следующим образом:

- (void) dealloc
{
     self.firstName = nil;
}

О @ "" - объекты типа. Они являются постоянными объектами NSStrings. Просто используйте их как были (они есть) объекты NSString и никогда не выпускайте их. Компилятор заботится о них.

1 голос
/ 13 июля 2009

Последняя часть первая: вам действительно придется автоматически / отпустить Me. Однако вам также придется добавить [firstName release]; и [lastName release]; в -dealloc; или еще лучше; self.firstName = nil;

Что касается строковых литералов; эта часть становится немного волосатой, но [@"String literal" release], по сути, просто тупик. Таким образом, - это разница между двумя временными объектами, но, поскольку вы, как правило, не будете знать, с каким вы будете иметь дело, добавьте [temp release]; как правило, это безопасный выбор, если только вы не знаете, что он будет содержать автоматически выпущенный объект.

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