diff --git a/SignalMessaging/contacts/OWSContactsManager.m b/SignalMessaging/contacts/OWSContactsManager.m index 8ab68c022..13057afc4 100644 --- a/SignalMessaging/contacts/OWSContactsManager.m +++ b/SignalMessaging/contacts/OWSContactsManager.m @@ -36,7 +36,7 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification @property (nonatomic, readonly) SystemContactsFetcher *systemContactsFetcher; @property (nonatomic, readonly) YapDatabaseConnection *dbReadConnection; @property (nonatomic, readonly) YapDatabaseConnection *dbWriteConnection; -@property (nonatomic, readonly) NSCache *cnContactCache; +@property (nonatomic, readonly) AnyLRUCache *cnContactCache; @end @@ -61,8 +61,7 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification _signalAccounts = @[]; _systemContactsFetcher = [SystemContactsFetcher new]; _systemContactsFetcher.delegate = self; - _cnContactCache = [NSCache new]; - _cnContactCache.countLimit = 50; + _cnContactCache = [[AnyLRUCache alloc] initWithMaxSize:50]; OWSSingletonAssert(); @@ -145,11 +144,14 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification return nil; } - CNContact *_Nullable cnContact = [self.cnContactCache objectForKey:contactId]; - if (!cnContact) { - cnContact = [self.systemContactsFetcher fetchCNContactWithContactId:contactId]; - if (cnContact) { - [self.cnContactCache setObject:cnContact forKey:contactId]; + CNContact *_Nullable cnContact; + @synchronized(self.cnContactCache) { + cnContact = (CNContact * _Nullable)[self.cnContactCache getWithKey:contactId]; + if (!cnContact) { + cnContact = [self.systemContactsFetcher fetchCNContactWithContactId:contactId]; + if (cnContact) { + [self.cnContactCache setWithKey:contactId value:cnContact]; + } } } diff --git a/SignalMessaging/utils/LRUCache.swift b/SignalMessaging/utils/LRUCache.swift index 26d616c44..4321d6845 100644 --- a/SignalMessaging/utils/LRUCache.swift +++ b/SignalMessaging/utils/LRUCache.swift @@ -24,26 +24,46 @@ public class AnyLRUCache: NSObject { } // A simple LRU cache bounded by the number of entries. -// -// TODO: We might want to observe memory pressure notifications. public class LRUCache { private var cacheMap: [KeyType: ValueType] = [:] private var cacheOrder: [KeyType] = [] private let maxSize: Int + @objc public init(maxSize: Int) { self.maxSize = maxSize + + NotificationCenter.default.addObserver(self, + selector: #selector(didReceiveMemoryWarning), + name: NSNotification.Name.UIApplicationDidReceiveMemoryWarning, + object: nil) + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + @objc func didReceiveMemoryWarning() { + SwiftAssertIsOnMainThread(#function) + + cacheMap.removeAll() + cacheOrder.removeAll() + } + + private func updateCacheOrder(key: KeyType) { + cacheOrder = cacheOrder.filter { $0 != key } + cacheOrder.append(key) } public func get(key: KeyType) -> ValueType? { guard let value = cacheMap[key] else { + // Miss return nil } - // Update cache order. - cacheOrder = cacheOrder.filter { $0 != key } - cacheOrder.append(key) + // Hit + updateCacheOrder(key: key) return value } @@ -51,9 +71,7 @@ public class LRUCache { public func set(key: KeyType, value: ValueType) { cacheMap[key] = value - // Update cache order. - cacheOrder = cacheOrder.filter { $0 != key } - cacheOrder.append(key) + updateCacheOrder(key: key) while cacheOrder.count > maxSize { guard let staleKey = cacheOrder.first else {