используя alloc и init - PullRequest
       21

используя alloc и init

6 голосов
/ 25 ноября 2010

Мы знаем о полном шаблоне alloc / init, который должен объединять alloc и init.

NSObject *myObj = [[NSObject alloc] init];

1 - метод init получает объект из другого источника (не из alloc, new, copy илитак или иначе) поэтому, согласно основному правилу управления памятью, он не является владельцем, и он не должен выпускать его.Тем не менее, «Распределение и инициализация объектов / Возвращенный объект» В статье говорится, что init может освободить получателя.

Как могэто возможно, если это противоречит основному правилу?

2- Также из той же статьи init может вернуть другой объект или ноль.Таким образом, в этом случае, когда мы используем полный шаблон alloc / init, мы не можем освободить объект, возвращенный alloc, но мы можем только освободить объект, возвращенный из init, и init освобождает объект, полученный от alloc вместо нас.

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

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

3- Или придерживаться последнего абзаца того жеarticle, Должны ли мы принять метод init как особый случай и использовать шаблон alloc / init как исключение из фундаментального правила?

Основное правило управления памятью:

  • Вы выпускаете или выпускаете только свои собственные объекты.
    • Вы вступаете во владение объектом, если создаете его с помощью метода, имя которого начинается с «alloc» или «new» или содержит «copy» (например, alloc, newObject или mutableCopy), или если выотправьте ему сообщение сохранения.
    • Вы используете release или autorelease, чтобы отказаться от владения объектом.autorelease просто означает «отправить сообщение о выпуске в будущем» (чтобы понять, когда это произойдет, см. «Пулы автоматического освобождения»).http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html

Распределение и инициализация объектов / Возвращаемый объект:

Однако в некоторых случаях эта ответственность может означать возвратдругой объект, чем приемник.Например, если класс хранит список именованных объектов, он может предоставить метод initWithName: для инициализации новых экземпляров.Если в имени может быть не более одного объекта, initWithName: может отказаться назначать одно имя двум объектам.Когда его просят назначить новому экземпляру имя, которое уже используется другим объектом, может освободить вновь выделенный экземпляр и вернуть другой объект - таким образом, обеспечивая уникальность имени, в то же время обеспечивая то, чтобыл запрошен экземпляр с запрошенным именем.

В некоторых случаях для метода init ... было бы невозможно выполнить то, что ему было предложено.Например, метод initFromFile: может получить необходимые данные из файла, переданного в качестве аргумента.Если переданное имя файла не соответствует реальному файлу, он не сможет завершить инициализацию.В таком случае метод init ... может освободить получателя и вернуть nil , указывая, что запрошенный объект не может быть создан.

Поскольку init ... метод может вернуть объект, отличный от вновь выделенного получателя, или даже вернуть nil , важно, чтобы программы использовали значение, возвращаемое методом инициализации, а не только значение, возвращаемое alloc или allocWithZone:. Следующий код очень опасен, так как он игнорирует возвращение init.

id anObject = [SomeClass alloc];
[anObject init];
[anObject someOtherMessage];

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

id anObject = [[SomeClass alloc] init];
[anObject someOtherMessage];

http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocAllocInit.html

Ответы [ 5 ]

8 голосов
/ 25 ноября 2010

Метод init не получает объект;объект получает сообщение init.Объект не обладает собой;скорее, он всегда знает о себе (через неявный аргумент self в каждом сообщении).

Вы правы в том, что объект сам по себе не принадлежит.Если бы alloc и init были объединены в одном методе new, этот метод был бы его собственным (super) вызывающим объектом, поэтому он будет владеть объектом (до тех пор, пока он не вернется) и, таким образом, будет безусловно прав в его освобождении,Поскольку они отделены друг от друга и init не является вызывающей стороной alloc, вы правы в том, что он не владеет объектом, поэтому вы правы, ставя под сомнение эту практику.

Это один изнесколько случаев, когда один объект может освободить объект (в данном случае сам) от имени другого.Альтернативой является не выпускать его, что, если вы собираетесь либо вернуть nil, либо выбросить исключение, будет утечкой.

В общем, всякий раз, когда у вас есть объект, сохраняющий или выпускающий себя,ты должен чувствовать себя грязным. В данном конкретном случае , все в порядке, потому что вы скорее предотвращаете ошибку (утечку), чем, возможно, ее создаете.

2 - Также из той же статьи init можетвернуть другой объект или ноль.Таким образом, в этом случае, когда мы используем полный шаблон alloc / init, мы не можем освободить объект, возвращенный alloc, но мы можем только освободить объект, возвращенный из init, и init освобождает объект, полученный от alloc вместо нас.

Но init - это не метод alloc, new, copy или подобное, поэтому мы не должны освобождать возвращаемый из него объект, поскольку он не дает нам права собственности на объект.

Asinit освобождает старый объект от имени своего вызывающего, если он создает новый объект, он делает это от имени своего вызывающего.Вызывающий объект владеет замещающим объектом, который init создает или извлекает для него.

В качестве следствия этого, если init возвращает ранее существующий объект, он должен сохранить его, чтобы вызывающий владелit.

Снова исследуя гипотетический метод * new, необходимо также освободить старый объект и создать (собственно) или сохранить замену.

Во всех этих случаяхinit действует от имени своего абонента.Обычно для одного метода сложно управлять другим, но для этих случаев необходимо init делать это от имени вызывающей стороны.

* Метод new существует, но просто отправляет alloc и init, поэтому нет необходимости реализовывать это отдельно.

3 голосов
/ 25 ноября 2010

Если по какой-либо причине инициализация завершается неудачно и должна возвращать ноль, вы должны освободить объект, чтобы избежать утечки памяти.

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

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

1 голос
/ 09 января 2012

[Поможет ли другая перспектива?] Метод init (и его родственные элементы initWith ... и подобные) немного странный, но не является особым случаем правил выделения памяти.Init странный, потому что у него есть имя, которое звучит так, как будто он изменит внутренности экземпляра, но на самом деле он может сделать больше, чем это (он может заменить некоторый другой объект и инициализировать этот объект, например).Подсказка содержится в объявлении init:

- (id)  init  // the way it is

vs

- (void) init  // in some other universe

Метод init возвращает объект, поэтому его лучше было бы назвать как-то как 'return a objectэто (для класса) эквивалентный объект, и это было инициализировано ».Большинство методов не выполняют этот тип переключения, что делает init немного другим / нечетным.

В вложении alloc / init нет ничего «волшебного» - это просто самый простой способ справиться с тем фактом, чтообъект, который вы возвращаете из alloc, может не совпадать с объектом, который вы получаете от init.Это прекрасно работает:

NSSomeClass* s = [NSSomeClass alloc];
s = [s init];  // that 's =' part is really important ;-)

и в точности соответствует «стандартной» идиоме:

NSSomeClass* s = [[NSSomeClass alloc] init];

Это потенциально проблематично:

NSSomeClass* s = [NSSomeClass alloc]
[s init];   // bad! you ignored the result of init

Реализацияметода init должен выполняться особенно осторожно, когда реализация возвращает объект, отличный от того, который он получает в качестве входящего «self».В таком случае метод init берет на себя ответственность за управление памятью объекта «self» (потому что он не собирается возвращать этот объект - так кто же еще может ожидать управление?)

ВозможноКстати, чтобы сделать довольно уродливый обман.Не пытайтесь делать это дома!

// don't do this!
S* s = [S alloc] 
[s retain]; // needs to survive 2 init's
S* ss = [s init......];  // s.retain goes 2-->1
S* sss = [s init.....];  //  ... goes 1-->0;

Это крайне плохая практика, потому что это зависит от метода init ....., всегда возвращающего новый объект (вместо того, который он получает).Это явно ошибочное предположение!

ПРИМЕЧАНИЕ: под "методом, получающим объект в" self ", я подразумеваю, что метод был вызван объектом / объектом, и этот объект сделан доступным по соглашению через 'указатель на себя.

0 голосов
/ 25 ноября 2010

И в-третьих, код - это больше, чем то, что вы бы назвали «руководящими принципами», а не фактическими правилами.

(капитан Барбоса)

alloc / init - это особый случай. Вы должны выполнить сохранение / освобождение внутри init так, чтобы любой объект, возвращаемый вызывающим, принадлежал вызывающему, и утечки не было.

0 голосов
/ 25 ноября 2010

Фундаментальное правило просто не применимо в этой особой ситуации.

На самом деле, не беспокойтесь об этом - если вы не планируете заниматься Posing, вам не нужно писать код, который это делает, и это никак не повлияет на код, который вы пишете.

Вы должны продолжать следовать основному правилу во всем своем коде.

...