#swift
#swift
Вопрос:
Я хочу получить доступ к NSCache из нескольких мест в моем приложении, поскольку я использую его для кэширования изображений с конечной точки API.
Например, table view 4 и viewcontroller 6 на диаграмме ниже используют одни и те же изображения, поэтому я не хочу загружать их дважды.
Возможные решения:
-
Синглтон
class Cache { private static var sharedCache: NSCache<AnyObject, AnyObject>? static public func getCache () -> NSCache<AnyObject, AnyObject> { if sharedCache == nil { self.sharedCache = NSCache() } return sharedCache! } }
Кажется, работает нормально, но «одиночные файлы плохие», так что…
- Сохраните кэш в TabViewController
Это позволит тесно связать представления с контроллером представления таким образом…
-
Каким-то образом сохранить в AppDelegate. Но разве это не то же самое, что 1? Итак…
-
Используйте внедрение зависимостей. Но мы находимся в контроллере просмотра вкладок, так разве это не то же самое, что 2?
Я не уверен, что здесь правильная стратегия, поэтому спрашиваю, есть ли другой метод, который можно использовать здесь.
Что я сделал, создал приложение с примером использования NSCache и исследовал одноэлементное решение. Я пытался использовать внедрение зависимостей, но думаю, что это не имеет смысла. Я просмотрел Stack overflow и документацию, но для этого конкретного обстоятельства я не нашел потенциальных решений.
То, что я привел, является минимальным примером со схемой и протестированным решением, которым я недоволен.
Что не помогает, так это ответы, в которых говорится, что NSCache неверен, или использовать библиотеки. Я пытаюсь использовать NSCache для собственного обучения, это не домашнее задание, и я хочу решить этот конкретный случай этой проблемы в этой структуре приложения.
В чем вопрос, как избежать использования синглтона в этом случае, контроллеры просмотра в контроллере просмотра вкладок.
Комментарии:
1. почему «одиночные файлы плохие»?
2. Аргумент antipattern — hackingwithswift.com/articles/88 /… — medium.com/@johnsundell / … — плюсы и минусы medium.freecodecamp.org / … — но проблема скорее в том, какова альтернатива?
3. Если единственной целью вашего кэша является хранение изображения в кэше, то вам следует рассмотреть возможность использования SDWebImage framework, который широко известен и используется для кэширования изображений
4. Что не помогает, так это ответы, в которых говорится, что NSCache неверен, или использовать библиотеки. Я пытаюсь использовать NSCache для собственного обучения, это не домашнее задание, и я хочу решить этот конкретный случай этой проблемы в этой структуре приложения.
Ответ №1:
Сначала. Синглтоны не являются изначально плохими. Они могут затруднить тестирование вашего кода и действуют как магниты зависимости.
Синглтоны хороши для классов, которые являются инструментами, например, NSFileManager
иначе FileManger
, то есть чем-то, что не переносит состояние или данные.
Хорошей альтернативой является внедрение зависимости, но с контроллерами просмотра и раскадровками это может быть сложно и казаться очень шаблонным. В конечном итоге вы передаете все по строке в prepareForSegue
.
Один из возможных методов — объявить protocol
, который описывает интерфейс, подобный кешу.
protocol CacheProtocol: class {
func doCacheThing()
}
class Cache: CacheProtocol {
func doCacheThing() {
//
}
}
Затем объявите protocol
, что все объекты, которые хотят использовать этот кэш, могут использовать.
protocol CacheConsumer: class {
var cache: CacheProtocol? { get set }
func injectCache(to object: AnyObject)
}
extension CacheConsumer {
func injectCache(to object: AnyObject) {
if let consumer = object as? CacheConsumer {
consumer.cache = cache
}
}
}
Наконец, создайте конкретный экземпляр этого кэша на верхнем уровне.
/// Top most controller
class RootLevelViewController: UIViewController, CacheConsumer {
var cache: CacheProtocol? = Cache()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
injectCache(to: segue.destination)
}
}
Вы могли бы передать кэш по строке в prepareForSegue
.
Или вы можете использовать тонкие подклассы для создания соответствия.
class MyTabBarController: UITabBarController, CacheConsumer {
var cache: CacheProtocol?
}
Или вы можете использовать методы делегирования, чтобы заставить объект кэша транслироваться вниз по склону.
extension RootLevelViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
injectCache(to: viewController)
}
}
Теперь у вас есть система, в которой любой CacheConsumer
может использовать кэш и передавать его по умолчанию любому другому объекту.
Ответ №2:
Если вы используете шаблон координатора, вы можете сохранить кэш в координаторе для вашего потока навигации и получить к нему доступ оттуда / инициализировать с помощью кэша. Это также работает хорошо, поскольку при удалении потока навигации кэш также удаляется.
final class SomeCoordinator: NSObject, Coordinator {
var rootViewController: UINavigationController
var myCache = NSCache<AnyObject, AnyObject>()
override init() {
self.rootViewController = UINavigationController()
super.init()
}
func start() {
let vc = VC1(cache: myCache)
vc.coordinator = self
rootViewController.setViewControllers([vc], animated: false)
parentCoordinator?.rootViewController.present(rootViewController, animated: true)
}
func goToVC2() {
let vc = VC2(cache: myCache)
vc.coordinator = self
rootViewController.pushViewController(vc, animated: true)
}
func goToVC3() {
let vc = VC3(cache: myCache)
vc.coordinator = self
rootViewController.present(vc, animated: true)
}
func goToVC4() {
let vc = VC4(cache: myCache)
vc.coordinator = self
rootViewController.present(vc, animated: true)
}
deinit {
print("✅ Deinit SomeCoordinator")
}
}