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

Прежде чем рассматривать эти правила, следует напомнить, что имя переменной, в том числе и экземпляра, представляет собой обычный указатель, что нередко смущает начинающих программировать в операционной системе iOS. Имя переменной, указывающее на объект, зачастую трактуется как объект, хотя оно таковым не является. Эти два понятия легко спутать, поэтому старайтесь не попасть в эту западню, неверно трактуя их. Когда сообщение посылается указателю, оно на самом деле посылается через этот указатель объекту, на который он указывает. Правила управления памятью относятся к объектам, а не к именам, ссылкам или указателям. Подсчет сохраняемых ссылок на указатель нельзя увеличить или уменьшить, поскольку такого объекта не существует. Память, занимаемая указателем, управляется автоматически и имеет крошеный объем. Управление памятью имеет отношение к объекту, на который ссылается указатель. Именно поэтому в примерах, рассматриваемых на страницах этой книги, объекты называются Manny, Мое, Jack или А и В, а не по именам переменных. Вопрос сохранения объекта Jack не имеет никакого отношения к имени, по которому любой другой объект обращается к объекту Jack.

Теперь вернемся к главному предмету этого раздела. Ниже приведены правила ручного управления памятью в среде Cocoa.

  • Для того чтобы увеличить подсчет сохраняемых ссылок на любой объект, достаточно послать ему сообщение retain. Эта операция называется сохранением объекта. Она гарантирует, что объект сохранится, по крайней мере, до тех пор, пока подсчет сохраняемых ссылок на него не уменьшится снова. Для большего удобства вызываемый метод retain возвращает в качестве своего значения сохраненный объект. Это означает, что в результате вызова [myObject retain] возвращается объект, на который указывает имя myOb j ect, но при этом подсчет сохраняемых ссылок этого объекта увеличивается.
  • Когда классу дается команда alloc (или new), начинает свое существование получающийся в итоге экземпляр этого класса, причем подсчет сохраняемых ссылок на него уже увеличен. Поэтому не нужно (да и не следует) сохранять объект, экземпляр которого только что получен по команде alloc или new. Аналогично, когда экземпляру дается команда сору, получающийся в итоге новый объект (т.е. копия) начинает свое существование, причем подсчет сохраняемых ссылок на него уже увеличен. Следовательно, не нужно (да и не следует) сохранять объект, экземпляр которого только что получен по команде сору.
  • Для того чтобы уменьшить подсчет сохраняемых ссылок на любой объект, достаточно отправить ему сообщение release. Эта операция называется освобождением объекта.
  • Если объект А получил объект В по команде alloc (либо new) или сору или же если объект А дал команду retain объекту В, то в конечном итоге объект А должен уравновесить эту операцию, выдав один раз команду release объекту В. После этого объект В должен принять во внимание, что объект В уже не существует. Это золотое правило управления памятью, позволяющее согласованно и правильно управлять ею.

Для того чтобы лучше понять золотое правило ручного управления памятью в среде Cocoa, следует мыслить категориями владения. Так, если объект Manny дал команду alloc, retain или сору объекту Jack, он тем самым подтвердил право на владение этим объектом. Одновременно объектом Jack могут владеть несколько объектов, но каждый такой объект отвечает только за правильное управление собственным владением объектом Jack. Объект, владеющий объектом Jack, отвечает в конечном счете за освобождение этого объекта, тогда как объект, не владеющий им, вообще не должен освобождать его. До тех пор, пока объекты, владеющие объектом Jack, ведут себя подобным образом, утечки памяти или висячие указатели на этот объект не возникнут.

Если действует механизм ARC, рассматриваемый в следующем разделе, эти правила остаются в силе, но они соблюдаются компилятором автоматически. В приложении на основе механизма ARC вообще не нужно, а на самом деле даже запрещено, давать команду retain или release вручную. Эти команды компилятор выдает автоматически, придерживаясь тех же самых принципов ручного управления памятью. Поскольку компилятор точнее и намного надежнее соблюдает правила управления памятью, чем вы как программист, то он вряд ли совершит ошибки, которые вы могли бы допустить по небрежности или недоразумению.

В тот момент, когда объект освобождается из памяти, появляется возможность уничтожить его. До появления механизма ARC это обстоятельство вызывало немалое беспокойство у программистов. В прикладной программе, не опирающейся на механизм ARC, приходится следить за тем, чтобы любые сообщения не посылались последовательно по любому указателю на объект, который уничтожен, включая и указатель, использованный для освобождения объекта. В противном случае такой указатель может стать висячим! Если существует какая-нибудь опасность попытаться случайно воспользоваться этим висячим указателем, то рекомендуется сделать его пустым, чтобы он указывал на пустое значение (nil). Сообщение, отправляемое по пустому указателю, не возымеет никакого действия. Оно не принесет никакой пользы, но и не нанесет большого вреда, как куриный бульон.

Данная стратегия строго соблюдается в прикладной программе, опирающейся на механизм ARC. В частности, механизм ARC делает пустым любой указатель на объект, которому было послано последнее уравновешивающее сообщение release, уведомляющее о том, что он может теперь прекратить свое существование. Как упоминалось в главе 3, механизм ARC делает также пустым указатель на экземпляр, когда он объявляется, если только не инициализировать его вручную, чтобы он указывал на конкретный экземпляр. Из этого неизбежно следует такой замечательный вывод: если действует механизм ARC, то всякий указатель на экземпляр указывает на конкретный экземпляр или на пустое значение. Но, к сожалению, это не избавляет полностью от появления висячих указателей, поскольку в самой среде Cocoa механизм ARC не применяется, как поясняется в следующем разделе.

 

Отладка ошибок управления памятью

Ошибки управления памятью относятся к числу наиболее распространенных ловушек, в которые попадают не только начинающие, но и опытные программирующие в среде Cocoa. Хотя такие ошибки совершаются намного реже, если действует механизм ARC, они все же вполне возможны, особенно потому, что программисты, пользующиеся механизмом ARC, склонны неверно думать, что они застрахованы от подобных ошибок. Практический опыт учит пользоваться всеми доступными средствами в поисках возможных ошибок. Ниже перечислены некоторые из этих средств (см. также главу 9).

  • Измеритель памяти в навигаторе отладки (Debug) предоставляет диаграмму использования памяти при выполнении приложения, позволяя следить за возможными утечками памяти или другим неоправданно чрезмерным ее использованием.
  • Статический анализатор, вызываемый по команде Product ^Analyze, предоставляет немало полезных сведений об управлении памятью и может помочь привлечь ваше внимание к потенциальным ошибкам управления памятью.
  • На панели Instruments (Инструменты), доступной по команде Product^Profile, имеются отличные средства для выявления утечек памяти и слежения за управлением памятью, выделяемой для отдельных объектов.
  • Проверенная временем простейшая самодиагностика помогает убедиться в том, что объекты ведут себя должным образом. В частности, реализуйте метод dealloc с вызовом функции NSLog (). Если она не вызывается, значит, объект не собирается прекращать свое существование. Такой прием позволяет выявить ошибки, которые не способен обнаружить ни статический анализатор, ни инструментальные средства на панели Instruments.
  • Особенно трудно выявить висячие указатели, но их зачастую можно обнаружить, активизировав объекты-зомби. Это нетрудно сделать на панели Instruments с помощью шаблона Zombies. С другой стороны, можно отредактировать действие Run в своей схеме, перейти на вкладку Diagnostics и установить флажок Enable Zombie Objects. В итоге ни один из объектов не прекратит свое существование, но вместо этого будет заменен объектом-зомби, выводящим на консоль уведомление вроде "Сообщение послано освобожденному из памяти экземпляру", если отправить ему сообщение. Не забудьте дезактивизировать объекты-зомби по завершении отслеживания висячих указателей. Не пользуйтесь объектами-зомби вместе с инструментом Leaks, поскольку эти объекты сами являются утечками.

Но даже помощи всех перечисленных выше инструментальных средств может оказаться недостаточно в разрешении всех затруднений, связанных с управлением памятью. Например, такие объекты, как представление типа UlView, содержащее крупное изображение, сами по себе невелики, а следовательно, в измерителе памяти или на панели Instruments может быть и не зарегистрировано чрезмерное использование памяти. Тем не менее они требуют значительного объема дополнительной памяти. Необходимость поддерживать ссылки на слишком большое количество подобных объектов может в конечном итоге привести к тому, что приложение будет просто уничтожено системой. Отслеживать подобные ошибки непросто.


 

 

 

Добавить комментарий