Как добавить живой объект в NSMutableArray и удалить их, когда они будут освобождены? - PullRequest
4 голосов
/ 12 мая 2011

У меня есть класс Item и класс List (который имеет NSMutableArray).

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

Я пытаюсь, чтобы класс Item также опубликовал уведомление о том, что он скоро будет удален. Проблема в том, что NSMutableArray класса List сохраняет экземпляр класса Item.

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

По сути, я хочу получить список «monitor» класса, который содержит список всех «живых» экземпляров Item. Но мне также нужна возможность освободить / освободить экземпляры и сделать так, чтобы они сообщали, что они были освобождены, чтобы List мог удалить их из своего NSMutableArray.

Спасибо за вашу помощь.

Ответы [ 2 ]

11 голосов
/ 12 мая 2011

Если я правильно понимаю, вам нужен массив, который содержит слабые ссылки на свои элементы, в отличие от сильных ссылок?

Я не знаю, как это сделать с помощью чего-либо "встроенного-в "в какао.Единственный способ, которым я знаю об этом, - создать массив самостоятельно и иметь хранилище __weak id[].Это автоматически обнуляет место в массиве, когда объект освобождается.Если вы используете модель с повторным выпуском, вы можете использовать что-то вроде MAZeroingWeakRef, чтобы получить то же поведение.

Это, безусловно, интересный вопрос, и я не знаю,более простого ответа.Я бы хотел оказаться неправым!

Ха, я люблю ошибаться!

Есть класс под названием NSPointerArray, который выглядит так, как можетчто ты ищешь.Тем не менее, он доступен только на Mac, и это только автоматические нули, когда вы используете сборщик мусора.

Я буду продолжать думать об этом.Это интересная проблема!:)


Поэтому я продолжал думать об этом и придумал решение.Он использует две нетрадиционные вещи:

  1. Подкласс NSMutableArray (egads!)
  2. Использование связанного объекта для определения освобождения объекта

ДляВо-первых, мне нужно было создать подкласс NSMutableArray, чтобы я мог внедрить некоторую пользовательскую логику в addObject: (и связанные методы).Я не хотел делать это через Swizzling, так как NSArray и друзья являются кластером классов, а вхождение в / из кластеров чревато опасностью.Итак, подкласс.Это нормально, но мы потеряем некоторые удивительные функции, которые мы получаем от "чистых" NSArray экземпляров, например , как они делают странные вещи, когда становятся большими .Ну что ж, такова жизнь.

Что касается второго бита, мне нужен был способ для любого произвольного объекта уведомить, что он собирается или только что завершил освобождение.Я подумал о том, чтобы динамически создать подкласс класса объекта, ввести свой собственный метод dealloc / finalize, вызвать super, а затем разбить isa объекта, но это просто показалось слишком сумасшедшим.

Итак, я решил воспользоваться небольшой забавной штукой, которая называется связанные объекты.Это ивары, которые относятся к классам: они позволяют динамически добавлять и удалять переменные псевдоэкземпляра во время выполнения.У них также есть удивительный побочный эффект автоматической очистки с освобождением объекта.Поэтому я просто создал небольшой выбрасываемый объект, который отправляет уведомление, когда it освобождается, а затем присоединяет его к обычному объекту.Таким образом, когда обычный объект освобождается, выбрасываемый объект также будет иметь место, в результате чего будет опубликовано уведомление, которое я затем прослушиваю в подклассе NSMutableArray.Уведомление содержит (устаревший) указатель на объект, который находится в процессе уничтожения, но, поскольку меня интересует только указатель , а не объект , все в порядке.

Результатом всего этого является то, что вы можете сделать:

DDAutozeroingArray *array = [DDAutozeroingArray array];

NSObject *o = [[NSObject alloc] init];
[array addObject:o];
NSLog(@"%ld", [array count]); //logs "1"
[o release];
NSLog(@"%ld", [array count]); //logs "0"

Источник находится на github, и он должен (теоретически) работать так же хорошо на iOS, как Mac OS X (независимо от того,Режим GC): https://github.com/davedelong/Demos

Приветствия!


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


на следующее утро ...

Я только что обновил проект на Github с категорией NSMutableArray, которая позволяет вамсоздать истинное значение NSMutableArray, которое автоматически обнуляет свои объекты по мере их освобождения.Трюк состоял в том, чтобы создать CFMutableArrayRef с пользовательским retain обратным вызовом, который устанавливает правильное наблюдение, а затем просто навести это CFMutableArrayRef на NSMutableArray и использовать это (ах, магия Toll-Free Bridging).

Это означает, что теперь вы можете:

NSMutableArray *array = [NSMutableArray autozeroingArray];

Я добавил typedef, чтобы определить их как NSAutozeroingMutableArray, просто чтобы четко дать понять, что, хотя это NSMutableArray, он не retain его объектов, как обычный NSMutableArray. Однако, поскольку это просто typedef, а не подкласс, вы можете использовать их взаимозаменяемо.

5 голосов
/ 12 мая 2011

Я не проверял это, поэтому комментарии приветствуются.

Вы можете использовать NSPointerArray для списка (в свойстве retain):

self.array = [NSPointerArray pointerArrayWithWeakObjects];

Когдаобъект Item создан, он будет публиковать уведомление, прослушиваемое вашим классом List.После получения уведомления List добавляет объект в массив указателей:

[array addPointer:pointerToTheObject];

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

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

В сборке без сбора мусора вам необходимо вручную удалить элемент или присвоить NULL позиции в массиве, где он был сохранен.Вы можете сделать это, переопределив -[Item dealloc] и разместив уведомление о том, что объект освобожден.Ваш класс List при получении уведомления будет действовать на него.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...