objective-c arc: сильный против удержания и слабый против присвоения

<p>в arc появилось два новых атрибута управления памятью для свойств: <code>strong</code> и <code>weak</code>.</p><p>помимо <code>copy</code>, который, очевидно, является чем-то совершенно другим, <strong>есть ли различия между <code>strong</code> vs <code>retain</code> и <code>weak</code> vs <code>assign</code>?</strong></p><p>насколько я понимаю, единственное отличие здесь состоит в том, что <code>weak</code> назначит указателю <code>nil</code>, а <code>assign</code> - нет, что означает, что программа завершится сбоем, когда я отправлю сообщение указателю после его освобождения. но если я использую <code>weak</code>, этого никогда не произойдет, потому что сообщение, отправленное <code>nil</code>, ничего не сделает.</p><p>я не знаю никаких различий между <code>strong</code> и <code>retain</code>.</p><p><strong>есть ли какая-то причина, по которой мне следует использовать <code>assign</code> и <code>retain</code> в новых проектах, или это не рекомендуется?</strong></p>

<p>от <a href="http://developer.apple.com/library/mac/#releasenotes/objectivec/rn-transitioningtoarc/introduction/introduction.html">переход к примечаниям к выпуску arc</a> (пример в разделе об атрибутах свойств).</p><pre><code>// the following declaration is a synonym for: @property(retain) myclass *myobject; @property(strong) myclass *myobject; </code></pre><p>таким образом, <code>strong</code> совпадает с <code>retain</code> в объявлении свойства.</p><p>для проектов arc я бы использовал <code>strong</code> вместо <code>retain</code>, я бы использовал <code>assign</code> для свойств примитива c и <code>weak</code> для слабых ссылок на объекты objective-c.</p>

<p>прочитав так много статей, опубликованных в stackoverflow и демонстрационных приложениях для проверки атрибутов переменных, я решил собрать всю информацию об атрибутах:</p><ol> <li>atomic // default</li> <li>неатомической</li> <li>strong = сохранить // по умолчанию</li> <li>слабый</li> <li>сохранить</li> <li>назначить // по умолчанию</li> <li>unsafe\_unretained</li> <li>копия</li> <li>только для чтения</li> <li>readwrite // по умолчанию</li> </ol><p>ниже приведена подробная ссылка на статью, где вы можете найти все вышеупомянутые атрибуты, которые определенно вам помогут. большое спасибо всем людям, которые дают лучшие ответы здесь!</p><blockquote> <p> <a href="http://rdcworld-iphone.blogspot.in/2012/12/variable-property-attributes-or.html"><strong> атрибуты свойства переменной или модификаторы в ios </strong></a> </p> </blockquote><p><strong> 1.сильный (ios4 = сохранить) </strong></p><ul> <li>там написано "держи это в куче, пока я не укажу на это"</li> <li>другими словами, "я владелец, вы не можете сдать это, пока не нацелились на то же самое, что и удержали"</li> <li>вы используете сильный, только если вам нужно сохранить объект.</li> <li>по умолчанию все переменные экземпляра и локальные переменные являются сильными указателями.</li> <li>обычно мы используем сильный для uiviewcontrollers (родители элемента ui)</li> <li>сильный используется с arc, и он в основном помогает вам, не заботясь о сохранении счета объекта. arc автоматически выпускает его для вас, когда вы закончите. использование ключевого слова strong означает, что вы являетесь владельцем объекта.</li> </ul><p>пример:</p><pre><code>@property (strong, nonatomic) viewcontroller *viewcontroller; @synthesize viewcontroller; </code></pre><p><strong> 2.слабый </strong> -</p><ul> <li>он говорит: "держите это, пока кто-то еще сильно на это указывает"</li> <li>то же самое, что присваивать, не сохранять или освобождать</li> <li>"слабая" ссылка - это ссылка, которую вы не сохраняете.</li> <li>как правило, мы используем слабый для iboutlets (childs uiviewcontroller). это работает, потому что дочерний объект должен существовать только столько, сколько родительский объект делает.</li> <li>слабая ссылка - это ссылка, которая не защищает указанный объект от сбора сборщиком мусора.</li> <li>слабым является, по сути, присваиваемое, оставшееся свойство. за исключением случаев, когда объект освобожден, слабый указатель автоматически устанавливается на ноль</li> </ul><p>пример :</p><pre><code>@property (weak, nonatomic) iboutlet uibutton *mybutton; @synthesize mybutton; </code></pre><p><strong> сильное и слабое объяснение, <a href="https://stackoverflow.com/a/9262768/19679">спасибо bj homer</a> : </strong></p><blockquote> <p>представьте, что наш объект - собака, и что собака хочет убежать (быть освобожденной).</p> <p>сильные указатели похожи на поводок на собаку. пока у вас есть поводок, привязанный к собаке, собака не убежит. если пять человек прикрепят поводок к одной собаке (пять сильных указателей на один объект), то собака не убежит, пока все пять поводков не отсоединятся.</p> <p>слабые указатели, с другой стороны, похожи на маленьких детей, указывающих на собаку и говорящих "смотри! собака!" пока собака все еще на поводке, маленькие дети все еще могут видеть собаку, и они все еще будут указывать на нее. однако, как только поводки отстегнуты, собака убегает независимо от того, сколько на нее указывают маленькие дети.</p> <p>как только последний сильный указатель (поводок) больше не указывает на объект, объект будет освобожден, а все слабые указатели будут обнулены.</p> </blockquote><p>когда мы используем слабые?</p><p>единственный раз, когда вы захотите использовать слабый, - это если вы хотите избежать циклов сохранения (например, родитель сохраняет ребенка, а ребенок сохраняет родителя, поэтому ни один из них никогда не освобождается).</p><p><strong> 3.retain = strong </strong></p><ul> <li>оно сохраняется, старое значение освобождается и ему присваивается сохранение, указывает, что новое значение должно быть отправлено</li> <li>сохранить при назначении и старое значение отправлено</li> <li>сохранить так же, как сильный.</li> <li>apple говорит, что если вы напишите "сохранить", он будет автоматически конвертироваться/работать как сильный.</li> <li>такие методы, как "alloc", включают в себя неявное "retain"</li> </ul><p>пример:</p><pre><code>@property (nonatomic, retain) nsstring *name; @synthesize name; </code></pre><p><strong> 4.assign </strong></p><ul> <li>assign является значением по умолчанию и просто выполняет присвоение переменной</li> <li>assign - это атрибут свойства, который сообщает компилятору, как синтезировать реализацию метода установки свойства</li> <li>я хотел бы использовать для примитивных свойств си и слабые для слабых ссылок на объекты objective-c.</li> </ul><p>пример:</p><pre><code>@property (nonatomic, assign) nsstring *address; @synthesize address; </code></pre>

<p>насколько я знаю, <code>strong</code> и <code>retain</code> являются синонимами, поэтому они делают <em> точно </em> то же самое.</p><p>тогда <code>weak</code> почти аналогичен <code>assign</code>, но автоматически устанавливается равным nil после того, как объект, на который он указывает, освобожден.</p><p><strong>это означает, что вы можете просто заменить их.</strong></p><p><strong>однако</strong>, есть один особый случай, с которым я столкнулся, когда мне пришлось использовать <code>assign</code>, а не <code>weak</code>. допустим, у нас есть два свойства <code>delegateassign</code> и <code>delegateweak</code>. в обоих хранится наш делегат, который владеет нами, имея единственную сильную ссылку. делегат освобождается, поэтому также вызывается наш метод <code>-dealloc</code>.</p><pre><code>// our delegate is deallocating and there is no other strong ref. - (void)dealloc { [delegateweak dosomething]; [delegateassign dosomething]; } </code></pre><p>делегат уже находится в процессе освобождения, но все еще не полностью освобожден. проблема в том, что <strong><code>weak</code> ссылки на него уже аннулированы!</strong> свойство <code>delegateweak</code> содержит nil, но <code>delegateassign</code> содержит действительный объект (все свойства уже освобождены и аннулированы, но все еще действительны).</p><pre><code>// our delegate is deallocating and there is no other strong ref. - (void)dealloc { [delegateweak dosomething]; // does nothing, already nil. [delegateassign dosomething]; // successful call. } </code></pre><p>это особый случай, но он показывает нам, как работают эти переменные <code>weak</code> и когда они обнуляются.</p>

<h2><strong>неатомической/атомарный</strong></h2><ul> <li>неатомный намного быстрее, чем атомный</li> <li>всегда используйте nonatomic, если у вас нет особых требований к atomic, что должно быть редко (atomic не гарантирует безопасность потока - блокирует доступ к свойству, только если он одновременно установлен другим потоком)</li> </ul><h2><strong>сильный/слабый/правопреемник</strong></h2><ul> <li>используйте <em> strong </em> для сохранения объектов - хотя ключевое слово retain является синонимом, лучше использовать вместо него сильное</li> <li>используйте <em> слабый </em>, если вам нужен только указатель на объект без его сохранения - полезно для избежания сохранения циклов (т. е. делегатов) - он автоматически обнулит указатель при освобождении объекта</li> <li>используйте <em> assign </em> для приматов - точно так же, как и слабый, за исключением того, что он не обнуляет объект при освобождении (устанавливается по умолчанию)</li> </ul><h1>(необязательный)</h1><h2><strong>копия</strong></h2><ul> <li>использовать его для создания мелкой копии объекта</li> <li>рекомендуется всегда задавать неизменяемые свойства для копирования - поскольку изменяемые версии могут передаваться в неизменяемые свойства, копирование гарантирует, что вы всегда будете иметь дело с неизменным объектом</li> <li>если передан неизменный объект, он сохранит его - если передан изменяемый объект, он скопирует его</li> </ul><h2><strong>только для чтения</strong></h2><ul> <li>используйте его, чтобы отключить настройку свойства (предотвращает компиляцию кода при нарушении)</li> <li>вы можете изменить то, что доставляет получатель, либо изменяя переменную напрямую через переменную экземпляра, либо внутри самого метода получателя.</li> </ul>

<p>в документе кланга о <a href="http://clang.llvm.org/docs/automaticreferencecounting.html">objective-c автоматический подсчет ссылок (arc)</a> четко объясняются квалификаторы и модификаторы владения:</p><blockquote> <p>существует четыре классификатора собственности:</p> <ul> <li>\_\_ <em> autoreleasing </em></li> <li>\_\_ <em> сильный </em></li> <li>\_\_ * unsafe\_unretained *</li> <li>\_\_ <em> слабо </em></li> </ul> <p>тип является нетривиально зависимым от владельца, если он квалифицирован с помощью \_\_ <em> автоматического освобождения </em>, \_\_ <em> strong </em> или \_\_ <em> слабо </em>.</p> </blockquote><p>тогда есть шесть модификаторов собственности для объявленной собственности:</p><blockquote> <ul> <li><em> assign </em> подразумевает владение \_\_ * unsafe\_unretained *.</li> <li><em> copy </em> подразумевает \_\_ <em> сильное владение </em>, а также обычное поведение семантики копирования в установщике.</li> <li><em> сохранить </em> подразумевает \_\_ <em> сильную </em> собственность.</li> <li><em> сильная </em> подразумевает \_\_ <em> сильную </em> собственность.</li> <li>* unsafe\_unretained * подразумевает \_\_ * unsafe\_unretained * владение.</li> <li><em> слабая </em> подразумевает \_\_ <em> слабую </em> собственность.</li> </ul> <p>за исключением <em> слабого </em>, эти модификаторы доступны в режимах без arc.</p> </blockquote><p>с точки зрения семантики квалификаторы владения имеют различное значение в <strong> пяти управляемых операциях </strong>: чтение, назначение, инициализация, уничтожение и перемещение, в которых в большинстве случаев мы только заботиться о разнице в операции назначения.</p><blockquote> <p><strong> присваивание </strong> происходит при оценке оператора присваивания. семантика варьируется в зависимости от квалификации:</p> <ul> <li>для объектов \_\_ <em> strong </em> новый pointee сначала сохраняется; во-вторых, lvalue загружается примитивной семантикой; в-третьих, новый pointee сохраняется в lvalue с примитивной семантикой; и, наконец, старый pointee освобожден. это не выполняется атомарно; внешняя синхронизация должна использоваться, чтобы сделать это безопасным перед лицом одновременных нагрузок и хранилищ.</li> <li>для \_\_ <em> слабых </em> объектов значение lvalue обновляется, чтобы указывать на новый указатель, если только этот указатель не является объектом, который в настоящее время подвергается освобождению, и в этом случае значение lvalue обновляется до нулевого указателя , это должно выполняться атомарно по отношению к другим присваиваниям объекту, чтению из объекта и окончательному выпуску нового pointee.</li> <li>для объектов \_\_ * unsafe\_unretained * новый pointee сохраняется в lvalue с использованием примитивной семантики.</li> <li>для объектов \_\_ <em> с автоматическим освобождением </em> новый pointee сохраняется, автоматически освобождается и сохраняется в lvalue с использованием примитивной семантики.</li> </ul> </blockquote><p>другое различие в чтении, инициировании, разрушении и перемещении, см. <a href="http://clang.llvm.org/docs/automaticreferencecounting.html#semantics">раздел 4.2 семантика в документе</a> .</p>

<p>чтобы понять сильные и слабые ссылки, рассмотрим пример ниже. предположим, у нас есть метод с именем displaylocalvariable.</p><pre><code> -(void)displaylocalvariable { nsstring myname = @"abc"; nslog(@"my name is = %@", myname); } </code></pre><p>в приведенном выше методе область действия переменной myname ограничена методом displaylocalvariable, как только метод завершится, переменная myname, содержащая строку "abc", будет освобождена из памяти.</p><p>теперь, что если мы хотим сохранить значение переменной myname на протяжении всего жизненного цикла контроллера представления. для этого мы можем создать свойство с именем username, которое будет иметь строгую ссылку на переменную myname (см. <code>self.username = myname;</code> в приведенном ниже коде), как показано ниже:</p><pre><code>@interface loginviewcontroller () @property(nonatomic,strong) nsstring* username; @property(nonatomic,weak) nsstring* dummyname; - (void)displaylocalvariable; @end @implementation loginviewcontroller - (void)viewdidload { [super viewdidload]; } -(void)viewwillappear:(bool)animated { [self displaylocalvariable]; } - (void)displaylocalvariable { nsstring myname = @"abc"; nslog(@"my name is = %@", myname); self.username = myname; } - (void)didreceivememorywarning { [super didreceivememorywarning]; } @end </code></pre><p>теперь в приведенном выше коде вы можете видеть, что myname было назначено для self.username, а self.username имеет сильную ссылку (как мы объявили в интерфейсе с использованием @property) на myname (косвенно это имеет сильную ссылку на строку "abc"). следовательно, строка myname не будет освобождена из памяти, пока self.username не станет активным.</p><ul> <li>слабая ссылка</li> </ul><p>теперь рассмотрите возможность присвоения myname dummyname, который является слабой ссылкой, self.dummyname = myname; в отличие от strong reference, weak будет хранить myname только до тех пор, пока не будет strong reference для myname. см. ниже код, чтобы понять слабую ссылку,</p><pre><code>-(void)displaylocalvariable { nsstring myname = @"abc"; nslog(@"my name is = %@", myname); self.dummyname = myname; } </code></pre><p>в вышеприведенном коде есть слабая ссылка на myname (т.е. self.dummyname имеет слабую ссылку на myname), но нет строгой ссылки на myname, поэтому self.dummyname не сможет содержать значение myname.</p><p>теперь снова рассмотрим приведенный ниже код,</p><pre><code>-(void)displaylocalvariable { nsstring myname = @"abc"; nslog(@"my name is = %@", myname); self.username = myname; self.dummyname = myname; } </code></pre><p>в приведенном выше коде self.username имеет ссылку strong на myname, следовательно, self.dummyname теперь будет иметь значение myname даже после завершения метода, поскольку myname имеет ссылку strong, связанную с ним.</p><p>теперь всякий раз, когда мы делаем ссылку strong на переменную, количество сохраняемых данных увеличивается на единицу, и переменная не освобождается, а количество сохраняемых данных достигает 0.</p><p>надеюсь это поможет.</p>

<h1>strong:</h1><ul> <li>свойство не будет уничтожено, но только после того, как вы установите свойство равным нулю, объект будет уничтожен</li> <li>по умолчанию все переменные экземпляра и локальные переменные являются сильными указателями.</li> <li>вы используете сильный, только если вам нужно сохранить объект.</li> <li>обычно мы используем сильный для uiviewcontrollers (родители элемента ui)</li> <li>ios 4 (не arc), который мы можем использовать, сохранить ключ</li> <li>ios 5(arc) мы можем использовать строгое ключевое слово</li> </ul><p>пример: @property (сильный, неатомный) viewcontroller * viewcontroller;</p><p>@synthesize viewcontroller;</p><h1>слабый</h1><p>по умолчанию автоматически получить и установить на ноль</p><ul> <li>обычно мы используем слабый для iboutlets (uiviewcontroller's childs) и делегировать</li> <li>то же самое, что присваивать, не сохранять или освобождать</li> </ul><p>пример: @property (слабый, неатомный) iboutlet uibutton * mybutton;</p><p>@synthesize mybutton;</p>

<p>различия между сильным и сохранением:</p><ul> <li>в ios4 сильный равен удержанию</li> <li>это означает, что вы владеете объектом и держите его в куче до тех пор, пока он больше не будет указывать на него.</li> <li>если вы напишите сохранить, он будет автоматически работать так же, как сильный</li> </ul><hr><p>различия между слабым и назначаемым:</p><ul> <li>"слабая" ссылка - это ссылка, которую вы не сохраняете и сохраняете до тех пор, пока кто-то на нее сильно указывает</li> <li>когда объект "освобожден", слабый указатель автоматически устанавливается на ноль</li> <li>атрибут свойства "assign" сообщает компилятору, как синтезировать реализацию метода установки свойства</li> </ul>