Я использую NSCache для хранения элементов, которые следует кэшировать по соображениям производительности, поскольку их воссоздание довольно дорого. Понимание поведения NSCache вызывает у меня некоторые головные боли.
Я инициализирую свой NSCache следующим образом:
_cellCache = [[NSCache alloc] init];
[_cellCache setDelegate:self];
[_cellCache setEvictsObjectsWithDiscardedContent:NO]; // never evict cells
Объекты, хранящиеся в кэше, реализуют протокол NSDiscardableContent. Теперь моя проблема в том, что класс NSCache, кажется, не работает правильно в нескольких случаях.
1) Сначала небольшая проблема. SetCountLimit NSCache: утверждает, что:
Установка ограничения количества на число, меньшее или равное 0, не повлияет на максимальный размер кэша.
Может кто-то пролить свет на то, что это значит? То есть, что NSCache максимален и будет выдавать сообщения discardContentIfPossible только тогда, когда в другом месте требуется память? Или, что NSCache минимален, и он немедленно выдаст discardContentIfPossible сообщений?
Первое имеет больше смысла, но, похоже, тестирование указывает на то, что происходит позже. Если я регистрирую вызовы метода discardContentIfPossible в моем кэшированном объекте, я вижу, что он вызывается почти сразу - после добавления в кэш только 2-3 десятков элементов (каждый менее 0,5 МБ ).
Хорошо. Поэтому я пытаюсь установить предел большого количества - гораздо больше, чем мне когда-либо понадобится, добавив следующую строку:
[_cellCache setCountLimit:10000000];
Тогда discardContentIfPossible сообщения больше не отправляются почти сразу. Я должен загрузить намного больше контента в кеш и использовать его некоторое время, прежде чем эти сообщения начнут появляться, что имеет больше смысла.
Так каково здесь предполагаемое поведение?
2) Большая проблема. В документации говорится:
По умолчанию объекты NSDiscardableContent в кэше автоматически удаляются из кэша, если их содержимое отбрасывается, хотя эту политику автоматического удаления можно изменить. Если объект NSDiscardableContent помещен в кеш, кеш вызывает discardContentIfPossible после его удаления.
Поэтому я установил политику выселения на NO (как указано выше), чтобы объекты никогда не выселялись. Вместо этого, когда вызывается discardContentIfPossible , я очищаю и освобождаю внутренние данные кэшированного объекта в соответствии со специальными критериями. То есть я могу принять решение не очищать и не сбрасывать данные при определенных обстоятельствах (например, если элемент использовался совсем недавно). В таком случае я просто возвращаюсь из метода discardContentIfPossible , ничего не отбрасывая. Идея состоит в том, что какой-то другой объект, который недавно не использовался, получит сообщение в какой-то момент и вместо этого может отказаться от его содержимого.
Теперь, что интересно, все, кажется, прекрасно работает некоторое время интенсивного использования. Загрузка большого количества контента, размещение и доступ к объектам в кеше и т. Д. Однако через некоторое время, когда я пытаюсь получить доступ к произвольному объекту в NSCache, его буквально нет! Каким-то образом кажется, что он был удален - хотя я специально установил политику выселения на NO.
Хорошо. Таким образом, реализация метода делегата cache: willEvictObject: показывает, что он никогда не вызывается. Что означает, что объект на самом деле не выселяют. Но он таинственным образом исчезает из NSCache, поскольку его нельзя найти в будущих поисках - или каким-то образом ключ, с которым он был связан, больше не отображается на исходный объект. Но как это может произойти?
Кстати, ключевой объект, который я связываю с моими объектами значений (CALayer), является контейнером / оболочкой NSURL, поскольку я не могу использовать NSURL напрямую, поскольку NSCache не копирует ключи - только сохраняет их, и более поздний ключ NSURL, используемый для поиска, может не совпадать с исходным объектом точный (только та же строка URL), который изначально использовался для загрузки кэша с этим объектом. Принимая во внимание, что с помощью оболочки / контейнера NSURL я могу гарантировать, что это именно тот объект исходного ключа, который использовался для добавления объекта исходного значения в NSCache.
Может кто-нибудь пролить свет на это?