Objective C управление памятью для анонимных объектов - PullRequest
2 голосов
/ 17 июня 2009

Я работаю над изучением Objective-C и пытаюсь почувствовать управление памятью. Я довольно хорошо знаком с управлением памятью в C, но я пытаюсь выяснить, насколько отличается ObjC.

Допустим, у меня есть класс с именем Complex, который используется для хранения комплексных чисел, у которого есть метод -(Complex*)add:(Complex*)c;, который добавляет переданное комплексное число к self (Complex является изменяемым объектом, скажем ).

Так что я могу назвать это так:

Complex *c = [[Complex alloc] withReal: -3.0 andImag: -2.4]; // c = -3.0-2.4i
[c add : [[Complex alloc] withReal: 1.0 andImag: 2.0]]; // now c = -2.0-0.4i

Что происходит с памятью, используемой для временного объекта, созданного при вызове add? Я предполагаю, что это утечка памяти; это правильный код?

Complex *c = [[Complex alloc] withReal: -3.0 andImag: -2.4]; // c = -3.0-2.4i
Complex *b = [[Complex alloc] withReal: 1.0 andImag: 2.0]; // b = 1+2i
[c add : b]; // now c = -2.0-0.4i
[b release];

Бонусный вопрос: будет ли Objective-C 2.0 GC справляться с ситуацией?

Ответы [ 3 ]

2 голосов
/ 17 июня 2009

Во-первых, когда я создаю пользовательские классы, при использовании моих «лучших практик» идиома Objective-C заключается в присвоении инициализаторам (эквивалентным «конструкторам» в других языках) ведущего «init». Кроме того, старайтесь не сокращать части селектора метода или именованные параметры.

Например, вместо этого:

- (id) withReal:(double)r andImag:(double)i { ... }

Вы должны рассмотреть возможность сделать это:

- (id) initWithReal:(double)real imaginary:(double)imaginary { ... }

«И» перед второй частью - дело вкуса - его вполне приемлемо использовать, но я предпочитаю его без, так как вы, вероятно, не будете использовать его для методов с параметрами 3+. Например, было бы более многословно, чем необходимо, если бы вы создавали цвет RGB для использования -initWithRed:andGreen:andBlue: - написание / использование -initWithRed:green:blue: обычно предпочтительнее.


Что касается вашего настоящего вопроса, я полностью согласен с тем, что создание конструктора вспомогательных классов - это разумный способ решения таких ситуаций. Визуально он намного чище, чем alloc-init-autorelease каждый раз, когда вам нужен одноразовый экземпляр. Соответствуя приведенным выше изменениям, я бы настроил ответ @ kent так:

+ (Complex*) complexWithReal:(double)real imaginary:(double)imaginary {
    return [[Complex alloc] initWithReal:real imaginary: imaginary] autorelease];
}

Что касается бонусного вопроса, да, GC Objective-C 2.0 справится с этим просто отлично; большинство хорошо написанного кода re-release работает как есть в GC, особенно если вы осторожны, если указатели равны нулю, когда они вам больше не нужны. Когда GC включен, вызовы retain / release / autorelease по существу не выполняются, и объекты собираются, когда на них нет ссылок. Как только последний указатель на объект потерян или выйдет за пределы локальной области видимости, объект будет готов к сбору. (Технически, GC учитывает только сильных ссылок, но если вы сознательно не используете слабые ссылки, различие, вероятно, пока не имеет значения.) Просто помните, что сборка мусора в настоящее время НЕ поддерживается на iPhone.

2 голосов
/ 17 июня 2009

В первом примере:

[c add : [[Complex alloc] withReal: 1.0 andImag: 2.0]];

Вы выделили объект с номером ссылки 1, и он не освобожден, поэтому это утечка памяти, поскольку вы не сохраняете указатель на этот объект, поэтому вы не можете отправить выпуск на него.

Вы можете изменить его для автоматического освобождения временного объекта:

[c add : [[[Complex alloc] withReal: 1.0 andImag: 2.0] autorelease]];

Вероятно, вы используете init в имени, чтобы указать на тот факт, что объект не является автоматически выпущенным, и пользователь должен управлять управлением памятью:

initWithReal:(double)r andImag:(double)i

Второй пример верен.

Какао предоставляет множество методов (как статических, так и членских), которые возвращают объекты с автоматически освобождением, например, [NSString stringWithString:@"example"], так что вы можете создавать методы, возвращающие объекты с автоматически освобождением, чтобы вам не приходилось иметь дело с освобождением:

Complex * c = [Complex complexWithReal:1.0 andImag:2.0]

Вы также можете создать метод, который принимает необработанные данные, например, что-то вроде addReal:(double)r andImag:(double)i, так что вы можете пропустить шаг alloc / init.

Другим вариантом является использование структур, используемых в Какао (CGRect и т. Д.). Они такие же, как в C, что означает, что они размещены в стеке, и они исчезают, как только выходят из области видимости, в отличие от выделенных объектов в куче с помощью alloc / init. Подходит для «маленьких» предметов, которые часто используются, но часто вы не хотите держать их рядом.

Что касается GC, я не вижу никаких причин, по которым GC не сможет справиться с освобождением объекта - хотя я не использовал его много (хотя смотрел только на пару примеров - я предпочитаю управлять памятью самостоятельно - если не использую python ...)

1 голос
/ 17 июня 2009

stefanB правильно. так как вы будете делать много из этого экземпляра «на лету» (я бы предположил ...), имело бы смысл построить статический конструктор «удобства» (есть множество примеров в appKit & co.)

+(Complex*) complexWithReal:(double)r andImag:(double)i
{
  return [[Complex alloc] initWithReal:r andImag:i] autorelease];
}

, который вызовет добавление сообщения и будет выглядеть так:

[c add : [Complex complexWithReal:2.0 andImage:1.0]]

веселись!

...