Кэширование экземпляров в Objective C - PullRequest
3 голосов
/ 06 января 2009

Я хочу кешировать экземпляры определенного класса. Класс хранит словарь всех своих экземпляров, и когда кто-то запрашивает новый экземпляр, класс сначала пытается удовлетворить запрос из кэша. Однако есть небольшая проблема с управлением памятью: словарный кеш сохраняет вставленные объекты, так что они никогда не освобождаются. Я хочу, чтобы они были освобождены, так что мне пришлось перегрузить метод release, и когда количество сохранений упадет до единицы, я могу удалить экземпляр из кэша и разрешить его освобождение.

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

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


Пояснение: Я не могу позволить себе хранить экземпляры в памяти, если они кому-то действительно не нужны, поэтому я хочу удалить экземпляр из кэша, когда последний «реальный» пользователь выпустит его.


Лучшее решение: Это было на iPhone, я хотел кэшировать некоторые текстуры и, с другой стороны, я хотел освободить их из памяти, как только их выпустил последний настоящий держатель. Проще всего это закодировать с помощью другого класса (назовем его TextureManager). Этот класс управляет экземплярами текстуры и кэширует их, чтобы последующие вызовы текстуры с тем же именем обслуживались из кэша. Нет необходимости немедленно очищать кэш, поскольку последний пользователь освобождает текстуру. Мы можем просто сохранить кеширование текстуры в памяти, а когда устройству не хватает памяти, мы получаем предупреждение о нехватке памяти и можем очистить кэш. Это лучшее решение, потому что материал для кэширования не загрязняет класс Texture, нам не нужно связываться с release, и вероятность попадания в кэш даже выше. TextureManager можно абстрагировать в ResourceManager, чтобы он мог кэшировать другие данные, а не только текстуры.

Ответы [ 6 ]

5 голосов
/ 13 января 2011

То, что вы хотите - это слабая ссылка на ноль (это не «Грааль алгоритмов управления кэшем», это хорошо известный паттерн). Проблема в том, что Objective C обеспечивает обнуление слабых ссылок только при запуске с сборкой мусора, а не в программах, управляемых вручную. И iPhone не обеспечивает сборку мусора (пока).

Кажется, что все ответы пока указывают вам на половину решения.

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

Это работает в целом, но с треском проваливается для бесплатных базовых объектов Core Foundation. К сожалению, единственное решение, если вам нужно распространить эту технику на бесплатные объекты с мостами, требует некоторого взлома и недокументированных вещей (см. здесь для кода и пояснений) и поэтому не может использоваться для iOS или программ, которые вы хотите продавать в Mac App Store.

Если вам нужно продавать в магазинах Apple и, следовательно, следует избегать недокументированных вещей, ваша лучшая альтернатива - реализовать заблокированный доступ к сохраняющемуся кешу, а затем очистить его для ссылок с текущим значением -retainCount, равным 1, когда вы хотите выпустить объем памяти. Пока все обращения к кешу осуществляются с удерживаемой блокировкой, если вы наблюдаете счет 1, удерживая блокировку, вы знаете, что никто не сможет воскресить объект, если вы удалите его из кеша (и, следовательно, освободите его ), прежде чем освободить замок.

Для iOS вы можете использовать UIApplicationDidReceiveMemoryWarningNotification, чтобы запустить очистку. На Mac вы должны реализовать свою собственную логику: может быть, просто периодическая проверка или даже просто периодическая очистка (оба решения также будут работать на iOS).

5 голосов
/ 06 января 2009

Да, вы можете использовать NSHashTable для создания словаря, который по сути не содержит слов. Кроме того, вы можете вызвать CFDictionaryCreate с NULL для освобождения и сохранить обратные вызовы. Затем вы можете просто типизировать результат в NSDictionary благодаря бесплатному мостовому соединению и использовать его точно так же, как обычный NSDictionary, за исключением того, что не возитесь с сохранением количества.

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

2 голосов
/ 11 мая 2010

Я только что реализовал подобные вещи, используя NSMutableDictionary и зарегистрировавшись для UIApplicationDidReceiveMemoryWarningNotification. По предупреждению о памяти я удаляю что-либо из словаря с retainCount 1 ...

1 голос
/ 12 мая 2010

Используйте [NSValue valueWithNonretainedObject:], чтобы обернуть экземпляр в NSValue и поместить его в словарь. В экземпляре метода dealloc удалите соответствующую запись из словаря. Не возиться с удержанием.

0 голосов
/ 06 января 2009

Я думаю, что я бы подошел к этому, чтобы поддерживать отдельный счет или флаг где-то, чтобы указать, используется ли объект в кэше или нет. Затем вы можете проверить это, когда закончите работу с объектом, или просто запускать проверку каждые n секунд, чтобы увидеть, нужно ли его освобождать или нет.

Я бы избегал любого решения, связанного с выпуском объекта перед удалением его из словаря (использование NSValue valueWithNonretainedObject: было бы другим способом сделать это). Это просто вызовет у вас проблемы в долгосрочной перспективе.

0 голосов
/ 06 января 2009

Насколько я понимаю, вы хотите реализовать алгоритмы управления кэшем Graal: отбрасывать элементы, которые больше не будут использоваться.

Возможно, вы захотите рассмотреть и другие критерии, такие как отбрасывание наименее востребованных предметов.

...