Статьи

Изучите Цель-C: День 5

Добро пожаловать в пятую часть этой серии на Objective-C. Сегодня мы рассмотрим управление памятью, элемент Objective-C (и многих других языков), который сбивает с толку новых программистов. Большинство языков сценариев (таких как PHP) автоматически заботятся об управлении памятью, но Objective-C требует, чтобы мы были осторожны с нашим использованием памяти и вручную создавали и освобождали пространство для наших объектов.

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

В Objective-C есть два метода управления памятью, первый — подсчет ссылок, а второй — сборка мусора. Вы можете думать о них как о ручном и автоматическом, поскольку подсчет ссылок — это код, добавленный программистом, а сборщик мусора — это система, автоматически управляющая нашей памятью. Важным примечанием является то, что сборка мусора не работает на iPhone, поэтому мы не будем смотреть, как она работает. Если вы хотите программировать для Mac, то стоит посмотреть документацию Apple, чтобы увидеть, как работает сборка мусора.

Итак, как мы управляем нашей памятью в наших приложениях? Прежде всего, когда мы используем память в нашем коде? Когда вы создаете экземпляр класса (объект), память выделяется, и наш объект теперь может работать правильно. Теперь один маленький объект может показаться не таким уж удачным, но когда ваши приложения растут в размере — это быстро становится огромной проблемой.

Давайте рассмотрим пример, скажем, у нас есть какое-то приложение для рисования, и каждая фигура, которую рисует пользователь, является отдельным объектом. Если пользователь нарисовал 100 фигур, то у нас в памяти осталось 100 объектов. Теперь предположим, что пользователь начинает заново и очищает экран, затем рисует еще 100 объектов. Если мы не будем должным образом управлять нашей памятью, мы получим 200 объектов, которые не занимаются ничем иным, как захватом памяти.

Мы противостоим этому путем подсчета ссылок. Когда мы создаем новый объект и используем alloc, наши объекты имеют счет сохранения 1. Если мы вызываем функцию сохранения для этого объекта, счет хранения теперь равен 2, и так далее. Если мы освобождаем объект, счет сохранения уменьшается до 1. Пока счет хранения не равен нулю, наш объект останется на месте, но когда счет сохранения достигнет нуля, система освобождает наш объект — освобождая память.

Существуют различные методы, которые вы можете вызвать, которые окажут некоторое влияние на управление памятью. Прежде всего, когда вы создаете объект, используя имя метода, которое содержит alloc, new или copy, вы вступаете во владение этим объектом. Это также верно, если вы используете метод retain для объекта. Когда вы отпускаете или автоматически выпускаете (подробнее об этом позже) объект, вы больше не становитесь владельцем этого объекта и не заботитесь о том, что с ним происходит.

Итак, если мы выделим объект, как это;

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

Поскольку мы создали наш объект с использованием alloc, наш объект car теперь имеет счет сохранения 1, что означает, что он не будет освобожден. Если бы также сохранить наш объект, как это;

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

Когда вы создали новый проект XCode, вы, возможно, заметили некоторый код, который появляется по умолчанию, который создает пул автоматического выпуска, до сих пор вы его игнорировали — теперь мы посмотрим, что он делает и где его использовать. ,

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

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

К настоящему времени вы должны быть в состоянии до некоторой степени сказать, что делает код выше; он создает экземпляр NSAutoReleasePool с именем pool, выделяет для него память и затем инициирует его, используя метод init.

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

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

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

Например, если у вас есть цикл, который создает два временных объекта для выполнения того, что вы хотите, если вы установите эти два объекта на автоматическое высвобождение, вы можете использовать их до тех пор, пока пул не отправит сообщение стока, и вам не придется беспокоиться о ручном освобождении освободить. У Apple есть отличный пример того, как вы используете этот вид вложенного пула авто-релизов в своей документации;

Источник: mmAutoreleasePools

В приведенном выше примере есть немного больше, чем нам нужно, но структура есть. Как вы можете видеть, когда приложение открывается и загружается main, создается пул автоматического выпуска, называемый пулом. Это означает, что что-либо, автоматически освобожденное до отправки пула, будет использовано для этого пула автоматического выпуска, если только оно не находится внутри пула автоматического выпуска (извините, если это звучит немного запутанно).

Внутри цикла создается другой пул авто-релизов, называемый loopPool. Этот пул сливается внутри цикла, поэтому все автоматически освобождаемое внутри цикла освобождается до того, как цикл повторяется (или заканчивается).

Внутренний пул авто-релизов абсолютно не влияет на внешний пул авто-релизов, вы можете вкладывать столько пулов авто-релизов, сколько вам нужно. Если бы мы использовали autorelease в цикле выше, но не имели отдельного пула autorelease, то все объекты, которые мы создавали, не будут освобождены до конца main. Таким образом, если цикл запускается 100 раз, у нас будет 100 объектов, занимающих память, которые еще не освобождены — вздутие нашего приложения.

Прежде чем мы закончим, давайте рассмотрим кое-что, что может помочь сделать управление памятью более легкой для восприятия главой. До сих пор, когда мы создавали объекты, мы помнили, сколько ссылок имеет объект и т. Д., Но мы никогда не видели фактического числа. Для целей обучения есть метод, который мы можем использовать, чтобы увидеть, сколько ссылок объект назвал retainCount. То, как мы печатаем retainCount для объекта, выглядит примерно так;

retainCount возвращает целое число, поэтому мы используем% d для отображения его в консоли. Есть редкие случаи (в которые мы не будем вдаваться), когда retainCount может быть неправильным и поэтому не следует полагаться на 100% программно. Он реализован только для отладки, поэтому приложение никогда не должно запускаться с использованием метода retainCount.

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

Apple имеет фантастическую библиотеку документации для разработчиков, доступную на их веб-сайте для разработчиков, которую я настоятельно рекомендую вам проверить, если вам неясно, что мы затронули сегодня. Мы постарались сделать учебник коротким и сфокусированным на лазере сегодня, чтобы помочь вам понять управление памятью без каких-либо дополнительных ошибок.

Вопросы приветствуются, как обычно.

Просто поэкспериментируйте с консолью, создав простой объект, который содержит несколько синтезированных переменных, создайте несколько экземпляров этого класса, а затем проверьте счет сохранения с помощью метода retainCount. Лучший способ понять управление памятью — запустить XCode и поэкспериментировать с alloc и сохранения, и т.д., помните, что сбои и ошибки не являются кирпичной стеной, поскольку в конечном итоге они помогут вам избежать ошибок в будущем.

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