Главная » iPhone, Objective-C, Программирование для iOS и MacOS » Управление  памятью Objective-C

0

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

Чтобы скопировать блок из стека в кучу, отправьте ему сообщение сору:

ArrayEnumerationBlock iVarDevowelizer = [devowelizer copy];

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

Методы, получающие блоки в аргументах (такие, как enumerateObjectsUsingBlock: класса NSArray или addObserverForName:object:queue: usingBlock: класса NSNotificationCenter), должны скопировать переданные блоки. Тем самым они создают указатели – и сильные ссылки – на эти блоки.

Итак,  блоки  можно  объявлять,  присваивать  значения  и  передавать,  как

переменные. Мы также видели, что блоки отчасти похожи на функции. А теперь мы отправляем блоку сообщение так, словно он является объектом.

Нахождение блока в куче и его «объектное» поведение создает некоторые проблемы с управлением памятью.

Что происходит с переменными, используемыми в блоке?

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

Для примитивных переменных это означает копирование и сохранение их в локальных переменных блока. Для указателей блок хранит сильную ссылку на все объекты, к которым он обращается. Это означает, что любые объекты, ссылка на которые хранится в блоке, заведомо будут существовать по крайней мере до тех пор, пока существует сам блок. (Вас интересовало, чем блоки отличаются от указателей на функции? Пусть-ка указатель на функцию попробует сделать нечто подобное!)

Вернемся к программе VowеlМovеmеnt. В блоке devowelizer упоминаются два объекта, созданных за пределами этого блока: newStrings (массив для хранения измененных версий строк) и string (текущая строка, копируемая для изменения).

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

Могут ли сильные ссылки привести к появлению циклических ссылок?

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

Могу ли я измeнить переменные, скопированные блоком?

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

переменная  counter:

    block int counter = 0;

void (^counterBlock)() = ^{ counter++; };

counterBlock(); // значение counter увеличевается до 1 counterBlock(); // значение counter увеличевается до 2

Без ключевого слова _blосk вы получите ошибку компиляции в определении блока, с сообщением о том, что значение counter не может изменяться.

Будущее блоков

Понять, как работают блоки и как их правильно использовать, может быть непросто. Однако блоки чрезвычайно полезны в приложениях, интенсивно использующих обработку событий, так часто встречающихся в программировании Мас и iOS. В программных интерфейсах Apple блоки используются все чаще. Например, библиотеки   ALAssetLibrary  и   GameKit  содержат   многочисленные   методы   с

использованием блоков. Старайтесь по возможности использовать блочные методы

Apple, чтобы привыкнуть к работе с блоками.

Упражнения

Анонимный блок

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

Если вам потребуется передать целое число методу (например, numberWithInt:

класса NSNumber),вы можете передать тип int анонимно:

// Вариант 1: Последовательное выполнение всех операций int i;

i = 5;

NSNumber *num = [NSNumber numberWithInt:i];

// Вариант 2: Значение передается без объявления переменной NSNumber *num = [NSNumber numberWithInt:5];

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

Измените упражнение этой главы так, чтобы блок передавался анонимно в аргументе enumerateObjectsUsingBlock:. Иначе говоря, вы оставляете блок, но избавляетесь от блочной переменной.

NSNоtifiсаtiоnСеntеr

В главе 24 метод addObserver:selector:name:object: класса NSNotificationCenter использовался для регистрации обратных вызовов через метод zoneChange:. Измените это упражнение так, чтобы вместо него использовался метод addObserverForName:object:queue:usingBlock:.   За   подробностями   обращайтесь   к

документации  разработчика.

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

Передаваемый блок должен получать один аргумент (NSNotification *) и не

возвращать ничего, как это делает zoneChange:.

В аргументе queue: можно передать nil; этот аргумент используется для синхронизации – тема, которая в книге не рассматривается.

Источник: Аарон Хилегас, «Objective-C. Программирование для iOS и MacOS», 2012 г.

По теме:

  • Комментарии