From 82aab5ea1290d71735eae71df86cc8d6b975a044 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 22 Jun 2021 11:50:42 -0300 Subject: [PATCH] Apply count limits to all NSCache, clean up caching. --- Signal.xcodeproj/project.pbxproj | 8 --- .../CV/CVComponentState.swift | 9 +-- .../ConversationView/Cells/CVMediaCache.swift | 36 +++-------- .../ConversationInputToolbar.m | 2 +- .../HomeView/ConversationListViewController.m | 2 +- .../views/GroupDescriptionPreviewView.swift | 7 ++- Signal/test/util/ImageCacheTest.swift | 62 ------------------- .../Stickers/StickerKeyboard.swift | 2 +- .../Stickers/StickerPackCollectionView.swift | 2 +- SignalMessaging/Wallpapers/Wallpaper.swift | 2 +- SignalMessaging/contacts/OWSContactsManager.m | 6 +- SignalMessaging/groups/GroupV2Params.swift | 6 +- .../groups/GroupV2UpdatesImpl.swift | 2 +- SignalMessaging/groups/GroupsV2Impl.swift | 2 +- SignalMessaging/utils/AvatarBuilder.swift | 15 +---- SignalMessaging/utils/ImageCache.swift | 57 ----------------- .../Discovery/ContactDiscoveryTask.swift | 4 +- .../src/Contacts/PhoneNumberUtil.m | 3 +- .../Stickers/DownloadStickerOperation.swift | 2 +- .../Messages/Stickers/StickerManager.swift | 2 +- .../src/Storage/FullTextSearchFinder.swift | 2 +- SignalServiceKit/src/Util/LRUCache.swift | 17 ++++- .../src/Util/ModelReadCache.swift | 22 ++++--- 23 files changed, 65 insertions(+), 207 deletions(-) delete mode 100644 Signal/test/util/ImageCacheTest.swift delete mode 100644 SignalMessaging/utils/ImageCache.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 377456117c..04c1194d49 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -153,7 +153,6 @@ 346129991FD1E4DA00532771 /* SignalApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 346129971FD1E4D900532771 /* SignalApp.m */; }; 346129A51FD1F09100532771 /* OWSContactsManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 346129A21FD1F09100532771 /* OWSContactsManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 346129A61FD1F09100532771 /* OWSContactsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 346129A31FD1F09100532771 /* OWSContactsManager.m */; }; - 346129AD1FD1F34E00532771 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346129AC1FD1F34E00532771 /* ImageCache.swift */; }; 346129AF1FD1F5D900532771 /* SystemContactsFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346129AE1FD1F5D900532771 /* SystemContactsFetcher.swift */; }; 346129B41FD1F7E800532771 /* OWSProfileManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 346129B11FD1F7E800532771 /* OWSProfileManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 346129B51FD1F7E800532771 /* OWSProfileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 346129B21FD1F7E800532771 /* OWSProfileManager.m */; }; @@ -515,7 +514,6 @@ 4556FA681F54AA9500AF40DD /* DebugUIProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4556FA671F54AA9500AF40DD /* DebugUIProfile.swift */; }; 455A16DD1F1FEA0000F86704 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 455A16DB1F1FEA0000F86704 /* Metal.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 455A16DE1F1FEA0000F86704 /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 455A16DC1F1FEA0000F86704 /* MetalKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; - 455AC69E1F4F8B0300134004 /* ImageCacheTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 455AC69D1F4F8B0300134004 /* ImageCacheTest.swift */; }; 45638BDC1F3DD0D400128435 /* DebugUICalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45638BDB1F3DD0D400128435 /* DebugUICalling.swift */; }; 45666F581D9B2880008FE134 /* OWSScrubbingLogFormatterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666F571D9B2880008FE134 /* OWSScrubbingLogFormatterTest.m */; }; 4579431E1E7C8CE9008ED0C0 /* Pastelog.m in Sources */ = {isa = PBXBuildFile; fileRef = 4579431D1E7C8CE9008ED0C0 /* Pastelog.m */; }; @@ -1193,7 +1191,6 @@ 346129981FD1E4DA00532771 /* SignalApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SignalApp.h; sourceTree = ""; }; 346129A21FD1F09100532771 /* OWSContactsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactsManager.h; sourceTree = ""; }; 346129A31FD1F09100532771 /* OWSContactsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsManager.m; sourceTree = ""; }; - 346129AC1FD1F34E00532771 /* ImageCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = ""; }; 346129AE1FD1F5D900532771 /* SystemContactsFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemContactsFetcher.swift; sourceTree = ""; }; 346129B11FD1F7E800532771 /* OWSProfileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSProfileManager.h; sourceTree = ""; }; 346129B21FD1F7E800532771 /* OWSProfileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSProfileManager.m; sourceTree = ""; }; @@ -1588,7 +1585,6 @@ 4556FA671F54AA9500AF40DD /* DebugUIProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DebugUIProfile.swift; sourceTree = ""; }; 455A16DB1F1FEA0000F86704 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; }; 455A16DC1F1FEA0000F86704 /* MetalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalKit.framework; path = System/Library/Frameworks/MetalKit.framework; sourceTree = SDKROOT; }; - 455AC69D1F4F8B0300134004 /* ImageCacheTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCacheTest.swift; sourceTree = ""; }; 45638BDB1F3DD0D400128435 /* DebugUICalling.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DebugUICalling.swift; sourceTree = ""; }; 45666F571D9B2880008FE134 /* OWSScrubbingLogFormatterTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSScrubbingLogFormatterTest.m; sourceTree = ""; }; 456D0FD51F63094D008499CD /* km */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = km; path = translations/km.lproj/Localizable.strings; sourceTree = ""; }; @@ -2471,7 +2467,6 @@ 3476236124081F8900D4FB6F /* GroupManager+SignalMessaging.swift */, 34E6003924251AD50026AD4B /* GroupViewUtils.swift */, 4C090A1A210FD9C7001FD7F9 /* HapticFeedback.swift */, - 346129AC1FD1F34E00532771 /* ImageCache.swift */, 4C9C50FD22F36FA40054A33F /* OutboundMessage+OWS.swift */, 34BEDB1421C80BC9007B0EAE /* OWSAnyTouchGestureRecognizer.h */, 34BEDB1521C80BCA007B0EAE /* OWSAnyTouchGestureRecognizer.m */, @@ -3993,7 +3988,6 @@ B660F6AD1C29868000687D6E /* FunctionalUtilTest.m */, 345AE2B52317048200DB6225 /* GRDBFinderTest.swift */, 4C9D347923679C13006A4307 /* GroupAndContactStreamTest.swift */, - 455AC69D1F4F8B0300134004 /* ImageCacheTest.swift */, 34C1A93A2656E904004FA478 /* MiscTest.swift */, 34843B25214327C9004DED45 /* OWSOrphanDataCleanerTest.m */, 45666F571D9B2880008FE134 /* OWSScrubbingLogFormatterTest.m */, @@ -5270,7 +5264,6 @@ 32FC387926295FA50086C32B /* LoopingVideoView.swift in Sources */, 3478506B1FD9B78A007B8332 /* NoopCallMessageHandler.swift in Sources */, 340872CA2239563500CB25B0 /* AttachmentApprovalViewController.swift in Sources */, - 346129AD1FD1F34E00532771 /* ImageCache.swift in Sources */, 32275883264367520037E73A /* SpamCaptchaViewController.swift in Sources */, 3465948424352FE900E5C510 /* OWSTableViewController.swift in Sources */, 3425A5A22631E051006D5863 /* CVText.swift in Sources */, @@ -5907,7 +5900,6 @@ 452D1AF12081059C00A67F7F /* StringAdditionsTest.swift in Sources */, 4C2A538C23C5462300D28CD8 /* CVMessageMappingTest.swift in Sources */, 4C4BC6C32102D697004040C9 /* ContactDiscoveryOperationTest.swift in Sources */, - 455AC69E1F4F8B0300134004 /* ImageCacheTest.swift in Sources */, 34E8A8D12085238A00B272B1 /* ProtoParsingTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Signal/src/ViewControllers/ConversationView/CV/CVComponentState.swift b/Signal/src/ViewControllers/ConversationView/CV/CVComponentState.swift index bd664954f9..abdc658894 100644 --- a/Signal/src/ViewControllers/ConversationView/CV/CVComponentState.swift +++ b/Signal/src/ViewControllers/ConversationView/CV/CVComponentState.swift @@ -1204,12 +1204,9 @@ fileprivate extension CVComponentState { // TODO: Now that we're caching the displayable text on the view items, // I'm not sure if we still need this cache. - private static let displayableTextCache: NSCache = { - let cache = NSCache() - // Cache the results for up to 1,000 messages. - cache.countLimit = 1000 - return cache - }() + // + // Cache the results for up to 1,000 messages. + private static let displayableTextCache = NSCache(countLimit: 1000) static func displayableText(cacheKey: String, mentionStyle: Mention.Style, diff --git a/Signal/src/ViewControllers/ConversationView/Cells/CVMediaCache.swift b/Signal/src/ViewControllers/ConversationView/Cells/CVMediaCache.swift index 766238c504..58db76110c 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/CVMediaCache.swift +++ b/Signal/src/ViewControllers/ConversationView/Cells/CVMediaCache.swift @@ -7,14 +7,8 @@ import Foundation @objc public class CVMediaCache: NSObject { - private static func buildMediaCache(countLimit: Int) -> NSCache { - let cache = NSCache() - cache.countLimit = countLimit - return cache - } - - private let stillMediaCache = buildMediaCache(countLimit: 16) - private let animatedMediaCache = buildMediaCache(countLimit: 8) + private let stillMediaCache = NSCache(countLimit: 16) + private let animatedMediaCache = NSCache(countLimit: 8) private let stillMediaViewCache = MediaInnerCache(maxSize: 12) private let animatedMediaViewCache = MediaInnerCache(maxSize: 6) @@ -84,15 +78,13 @@ public class CVMediaCache: NSObject { private class MediaInnerCache { - private let maxSize: UInt - - private var cache = OrderedDictionary() + private var cache: LRUCache @objc - public required init(maxSize: UInt = 0) { + public required init(maxSize: Int = 0) { AssertIsOnMainThread() - self.maxSize = maxSize + cache = LRUCache(maxSize: maxSize) } // MARK: - API @@ -100,30 +92,18 @@ private class MediaInnerCache { func get(_ key: KeyType) -> ValueType? { AssertIsOnMainThread() - guard let value = cache[key] else { - return nil - } - cache.moveExistingKeyToFirst(key) - return value + return cache.get(key: key) } func set(value: ValueType, forKey key: KeyType) { AssertIsOnMainThread() - guard maxSize > 0 else { - return - } - - cache.remove(key: key) - cache.prepend(key: key, value: value) - if cache.count > maxSize { - cache.removeSubrange(Int(maxSize)...) - } + cache.set(key: key, value: value) } func removeAllObjects() { AssertIsOnMainThread() - cache.removeAll() + cache.clear() } } diff --git a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m index ad919d512a..e1abc00809 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m @@ -127,7 +127,7 @@ const CGFloat kMaxIPadTextViewHeight = 142; _conversationStyle = conversationStyle; _receivedSafeAreaInsets = UIEdgeInsetsZero; - _suggestedStickerViewCache = [NSCache new]; + _suggestedStickerViewCache = [[NSCache alloc] initWithCountLimit:12]; self.inputToolbarDelegate = inputToolbarDelegate; diff --git a/Signal/src/ViewControllers/HomeView/ConversationListViewController.m b/Signal/src/ViewControllers/HomeView/ConversationListViewController.m index 71e2709496..d357b3615d 100644 --- a/Signal/src/ViewControllers/HomeView/ConversationListViewController.m +++ b/Signal/src/ViewControllers/HomeView/ConversationListViewController.m @@ -125,7 +125,7 @@ NSString *const kArchiveButtonPseudoGroup = @"kArchiveButtonPseudoGroup"; { _blocklistCache = [OWSBlockListCache new]; [_blocklistCache startObservingAndSyncStateWithDelegate:self]; - _threadViewModelCache = [NSCache new]; + _threadViewModelCache = [[NSCache alloc] initWithCountLimit:32]; _threadMapping = [ThreadMapping new]; } diff --git a/Signal/src/views/GroupDescriptionPreviewView.swift b/Signal/src/views/GroupDescriptionPreviewView.swift index 8645c9cb71..605b40900e 100644 --- a/Signal/src/views/GroupDescriptionPreviewView.swift +++ b/Signal/src/views/GroupDescriptionPreviewView.swift @@ -69,7 +69,8 @@ class GroupDescriptionPreviewView: ManualLayoutView { ) private static let moreTextPlusPrefixLength = (moreTextPrefix + moreText).utf16.count - private static let textThatFitsCache = NSCache() + private let textThatFitsCache = NSCache(countLimit: 128) + func truncateVisibleTextIfNecessary() { // When using autolayout, we need to initially set the text // to the full text otherwise the view will never get any width. @@ -85,7 +86,7 @@ class GroupDescriptionPreviewView: ManualLayoutView { // If we have already determine the attributed text for // this size + description, use it. - if let cachedText = Self.textThatFitsCache.object(forKey: cacheKey) { + if let cachedText = textThatFitsCache.object(forKey: cacheKey) { return setTextThatFits(cachedText as String) } @@ -94,7 +95,7 @@ class GroupDescriptionPreviewView: ManualLayoutView { setTextThatFits(textThatFits) // Cache the text that fits for this size + description. - Self.textThatFitsCache.setObject(textThatFits as NSString, forKey: cacheKey) + textThatFitsCache.setObject(textThatFits as NSString, forKey: cacheKey) } setTextThatFits(textThatFits) diff --git a/Signal/test/util/ImageCacheTest.swift b/Signal/test/util/ImageCacheTest.swift deleted file mode 100644 index cc67d37459..0000000000 --- a/Signal/test/util/ImageCacheTest.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// Copyright (c) 2020 Open Whisper Systems. All rights reserved. -// - -import XCTest -@testable import Signal - -class ImageCacheTest: SignalBaseTest { - - var imageCache: ImageCache! - - let firstVariation = UIImage(color: .red, size: CGSize(square: 1)) - let secondVariation = UIImage(color: .blue, size: CGSize(square: 1)) - let otherImage = UIImage(color: .green, size: CGSize(square: 1)) - - let cacheKey1 = "cache-key-1" as NSString - let cacheKey2 = "cache-key-2" as NSString - - override func setUp() { - super.setUp() - - self.imageCache = ImageCache() - imageCache.setImage(firstVariation, forKey: cacheKey1, diameter: 100) - imageCache.setImage(secondVariation, forKey: cacheKey1, diameter: 200) - imageCache.setImage(otherImage, forKey: cacheKey2, diameter: 100) - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testSetGet() { - XCTAssertEqual(firstVariation, imageCache.image(forKey: cacheKey1, diameter: 100)) - XCTAssertEqual(secondVariation, imageCache.image(forKey: cacheKey1, diameter: 200)) - XCTAssertNotEqual(secondVariation, imageCache.image(forKey: cacheKey1, diameter: 100)) - XCTAssertEqual(otherImage, imageCache.image(forKey: cacheKey2, diameter: 100)) - XCTAssertNil(imageCache.image(forKey: cacheKey2, diameter: 200)) - } - - func testRemoveAllForKey() { - // sanity check - XCTAssertEqual(firstVariation, imageCache.image(forKey: cacheKey1, diameter: 100)) - XCTAssertEqual(otherImage, imageCache.image(forKey: cacheKey2, diameter: 100)) - - imageCache.removeAllImages(forKey: cacheKey1) - - XCTAssertNil(imageCache.image(forKey: cacheKey1, diameter: 100)) - XCTAssertNil(imageCache.image(forKey: cacheKey1, diameter: 200)) - XCTAssertEqual(otherImage, imageCache.image(forKey: cacheKey2, diameter: 100)) - } - - func testRemoveAll() { - XCTAssertEqual(firstVariation, imageCache.image(forKey: cacheKey1, diameter: 100)) - - imageCache.removeAllImages() - - XCTAssertNil(imageCache.image(forKey: cacheKey1, diameter: 100)) - XCTAssertNil(imageCache.image(forKey: cacheKey1, diameter: 200)) - XCTAssertNil(imageCache.image(forKey: cacheKey2, diameter: 100)) - } -} diff --git a/SignalMessaging/ViewControllers/Stickers/StickerKeyboard.swift b/SignalMessaging/ViewControllers/Stickers/StickerKeyboard.swift index eb37614773..91d12aca89 100644 --- a/SignalMessaging/ViewControllers/Stickers/StickerKeyboard.swift +++ b/SignalMessaging/ViewControllers/Stickers/StickerKeyboard.swift @@ -81,7 +81,7 @@ public class StickerKeyboard: CustomKeyboard { updatePageConstraints() } - private let reusableStickerViewCache = NSCache() + private let reusableStickerViewCache = NSCache(countLimit: 32) private func reloadStickers() { let oldStickerPacks = stickerPacks diff --git a/SignalMessaging/ViewControllers/Stickers/StickerPackCollectionView.swift b/SignalMessaging/ViewControllers/Stickers/StickerPackCollectionView.swift index b97008703f..12408696b9 100644 --- a/SignalMessaging/ViewControllers/Stickers/StickerPackCollectionView.swift +++ b/SignalMessaging/ViewControllers/Stickers/StickerPackCollectionView.swift @@ -235,7 +235,7 @@ public class StickerPackCollectionView: UICollectionView { return StickerView.stickerView(forStickerInfo: stickerInfo, dataSource: stickerPackDataSource) } - private let reusableStickerViewCache = NSCache() + private let reusableStickerViewCache = NSCache(countLimit: 32) private func reusableStickerView(forStickerInfo stickerInfo: StickerInfo) -> StickerReusableView { let view: StickerReusableView = { if let view = reusableStickerViewCache.object(forKey: stickerInfo) { return view } diff --git a/SignalMessaging/Wallpapers/Wallpaper.swift b/SignalMessaging/Wallpapers/Wallpaper.swift index b421db8a25..4687aacf44 100644 --- a/SignalMessaging/Wallpapers/Wallpaper.swift +++ b/SignalMessaging/Wallpapers/Wallpaper.swift @@ -298,7 +298,7 @@ extension Wallpaper { fileprivate extension Wallpaper { static let appSharedDataDirectory = URL(fileURLWithPath: OWSFileSystem.appSharedDataDirectoryPath()) static let wallpaperDirectory = URL(fileURLWithPath: "Wallpapers", isDirectory: true, relativeTo: appSharedDataDirectory) - static let cache = NSCache() + static let cache = NSCache(countLimit: 3) static func ensureWallpaperDirectory() throws { guard OWSFileSystem.ensureDirectoryExists(wallpaperDirectory.path) else { diff --git a/SignalMessaging/contacts/OWSContactsManager.m b/SignalMessaging/contacts/OWSContactsManager.m index cbba07b45c..da6d059a5b 100644 --- a/SignalMessaging/contacts/OWSContactsManager.m +++ b/SignalMessaging/contacts/OWSContactsManager.m @@ -66,10 +66,8 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan _signalAccounts = @[]; _systemContactsFetcher = [SystemContactsFetcher new]; _systemContactsFetcher.delegate = self; - _cnContactCache = [NSCache new]; - _cnContactCache.countLimit = 50; - _cnContactAvatarCache = [NSCache new]; - _cnContactAvatarCache.countLimit = 25; + _cnContactCache = [[NSCache alloc] initWithCountLimit:50]; + _cnContactAvatarCache = [[NSCache alloc] initWithCountLimit:25]; OWSSingletonAssert(); diff --git a/SignalMessaging/groups/GroupV2Params.swift b/SignalMessaging/groups/GroupV2Params.swift index 8b0456ef64..3d0377fc04 100644 --- a/SignalMessaging/groups/GroupV2Params.swift +++ b/SignalMessaging/groups/GroupV2Params.swift @@ -66,7 +66,7 @@ public extension GroupV2Params { return ciphertext } - private static let decryptedBlobCache = NSCache() + private static let decryptedBlobCache = NSCache(countLimit: 16) private static let decryptedBlobCacheMaxItemSize: UInt = 4 * 1024 fileprivate func decryptBlob(_ ciphertext: Data) throws -> Data { @@ -90,7 +90,7 @@ public extension GroupV2Params { return try uuid(forUuidCiphertext: uuidCiphertext) } - private static let decryptedUuidCache = NSCache() + private static let decryptedUuidCache = NSCache(countLimit: 256) func uuid(forUuidCiphertext uuidCiphertext: UuidCiphertext) throws -> UUID { let cacheKey = (groupSecretParamsData + uuidCiphertext.serialize().asData) as NSData @@ -117,7 +117,7 @@ public extension GroupV2Params { return userId } - private static let decryptedProfileKeyCache = NSCache() + private static let decryptedProfileKeyCache = NSCache(countLimit: 256) func profileKey(forProfileKeyCiphertext profileKeyCiphertext: ProfileKeyCiphertext, uuid: UUID) throws -> Data { diff --git a/SignalMessaging/groups/GroupV2UpdatesImpl.swift b/SignalMessaging/groups/GroupV2UpdatesImpl.swift index 2fbd6f41f5..ede45f381d 100644 --- a/SignalMessaging/groups/GroupV2UpdatesImpl.swift +++ b/SignalMessaging/groups/GroupV2UpdatesImpl.swift @@ -398,7 +398,7 @@ public class GroupV2UpdatesImpl: NSObject, GroupV2UpdatesSwift { } } - private let changeCache = NSCache() + private let changeCache = NSCache(countLimit: 5) private class ChangeCacheItem: NSObject { let groupChanges: [GroupV2Change] diff --git a/SignalMessaging/groups/GroupsV2Impl.swift b/SignalMessaging/groups/GroupsV2Impl.swift index 510e985728..2e49fa75c7 100644 --- a/SignalMessaging/groups/GroupsV2Impl.swift +++ b/SignalMessaging/groups/GroupsV2Impl.swift @@ -1478,7 +1478,7 @@ public class GroupsV2Impl: NSObject, GroupsV2Swift { } } - private let groupInviteLinkPreviewCache = NSCache() + private let groupInviteLinkPreviewCache = NSCache(countLimit: 5) private func groupInviteLinkPreviewCacheKey(groupSecretParamsData: Data) -> NSData { groupSecretParamsData as NSData diff --git a/SignalMessaging/utils/AvatarBuilder.swift b/SignalMessaging/utils/AvatarBuilder.swift index 293b5c0504..74cf9ce1a5 100644 --- a/SignalMessaging/utils/AvatarBuilder.swift +++ b/SignalMessaging/utils/AvatarBuilder.swift @@ -466,7 +466,7 @@ public class AvatarBuilder: NSObject { } // TODO: Tune configuration of this NSCache. - private let requestToContentCache: NSCache = NSCache(countLimit: 1024) + private let requestToContentCache = NSCache(countLimit: 1024) private func avatarContent(forRequest request: Request, transaction: SDSAnyReadTransaction) -> AvatarContent? { @@ -487,7 +487,7 @@ public class AvatarBuilder: NSObject { } // TODO: Tune configuration of this NSCache. - private let contentToImageCache: NSCache = NSCache(countLimit: 128) + private let contentToImageCache = NSCache(countLimit: 128) private func avatarImage(forAvatarContent avatarContent: AvatarContent) -> UIImage? { let cacheKey = avatarContent.cacheKey @@ -1058,14 +1058,3 @@ extension AvatarBuilder { } } } - -// MARK: - - -@objc -public extension NSCache { - convenience init(countLimit: Int) { - self.init() - - self.countLimit = countLimit - } -} diff --git a/SignalMessaging/utils/ImageCache.swift b/SignalMessaging/utils/ImageCache.swift deleted file mode 100644 index 2a8520717c..0000000000 --- a/SignalMessaging/utils/ImageCache.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// - -import Foundation -import UIKit - -class ImageCacheRecord: NSObject { - var variations: [CGFloat: UIImage] - init(variations: [CGFloat: UIImage]) { - self.variations = variations - } -} - -/** - * A two dimensional hash, allowing you to store variations under a single key. - * This is useful because we generate multiple diameters of an image, but when we - * want to clear out the images for a key we want to clear out *all* variations. - */ -@objc -public class ImageCache: NSObject { - - let backingCache: NSCache - - public override init() { - self.backingCache = NSCache() - } - - @objc - public func image(forKey key: AnyObject, diameter: CGFloat) -> UIImage? { - guard let record = backingCache.object(forKey: key) else { - return nil - } - return record.variations[diameter] - } - - @objc - public func setImage(_ image: UIImage, forKey key: AnyObject, diameter: CGFloat) { - if let existingRecord = backingCache.object(forKey: key) { - existingRecord.variations[diameter] = image - backingCache.setObject(existingRecord, forKey: key) - } else { - let newRecord = ImageCacheRecord(variations: [diameter: image]) - backingCache.setObject(newRecord, forKey: key) - } - } - - @objc - public func removeAllImages() { - backingCache.removeAllObjects() - } - - @objc - public func removeAllImages(forKey key: AnyObject) { - backingCache.removeObject(forKey: key) - } -} diff --git a/SignalServiceKit/src/Contacts/Discovery/ContactDiscoveryTask.swift b/SignalServiceKit/src/Contacts/Discovery/ContactDiscoveryTask.swift index 09f03ee36f..31acdf9ed4 100644 --- a/SignalServiceKit/src/Contacts/Discovery/ContactDiscoveryTask.swift +++ b/SignalServiceKit/src/Contacts/Discovery/ContactDiscoveryTask.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2020 Open Whisper Systems. All rights reserved. +// Copyright (c) 2021 Open Whisper Systems. All rights reserved. // import Foundation @@ -175,7 +175,7 @@ public extension ContactDiscoveryTask { public extension ContactDiscoveryTask { private static let unfairLock = UnfairLock() - private static let undiscoverableUserCache = NSCache() + private static let undiscoverableUserCache = NSCache(countLimit: 256) fileprivate static func markUsersAsRecentlyKnownToBeUndiscoverable(_ addresses: [SignalServiceAddress]) { guard !addresses.isEmpty else { diff --git a/SignalServiceKit/src/Contacts/PhoneNumberUtil.m b/SignalServiceKit/src/Contacts/PhoneNumberUtil.m index 737c00c5cc..4521176da5 100644 --- a/SignalServiceKit/src/Contacts/PhoneNumberUtil.m +++ b/SignalServiceKit/src/Contacts/PhoneNumberUtil.m @@ -5,6 +5,7 @@ #import #import #import +#import #import NS_ASSUME_NONNULL_BEGIN @@ -37,7 +38,7 @@ NS_ASSUME_NONNULL_BEGIN if (self) { _nbPhoneNumberUtil = [[NBPhoneNumberUtil alloc] init]; _countryCodesFromCallingCodeCache = [NSMutableDictionary new]; - _parsedPhoneNumberCache = [NSCache new]; + _parsedPhoneNumberCache = [[NSCache alloc] initWithCountLimit:256]; } return self; diff --git a/SignalServiceKit/src/Messages/Stickers/DownloadStickerOperation.swift b/SignalServiceKit/src/Messages/Stickers/DownloadStickerOperation.swift index 89c6ec3a81..b490cd80cf 100644 --- a/SignalServiceKit/src/Messages/Stickers/DownloadStickerOperation.swift +++ b/SignalServiceKit/src/Messages/Stickers/DownloadStickerOperation.swift @@ -9,7 +9,7 @@ class DownloadStickerOperation: CDNDownloadOperation { // MARK: - Cache - private static let cache = NSCache() + private static let cache = NSCache(countLimit: 256) public class func cachedUrl(for stickerInfo: StickerInfo) -> URL? { guard let stickerUrl = cache.object(forKey: stickerInfo.asKey() as NSString) else { diff --git a/SignalServiceKit/src/Messages/Stickers/StickerManager.swift b/SignalServiceKit/src/Messages/Stickers/StickerManager.swift index 0675136eb2..25992fec36 100644 --- a/SignalServiceKit/src/Messages/Stickers/StickerManager.swift +++ b/SignalServiceKit/src/Messages/Stickers/StickerManager.swift @@ -873,7 +873,7 @@ public class StickerManager: NSObject { private static let cacheQueue = DispatchQueue(label: "stickerManager.cacheQueue") // This cache shoud only be accessed on cacheQueue. - private var suggestedStickersCache = NSCache() + private var suggestedStickersCache = NSCache(countLimit: 5) // We clear the cache every time we install or uninstall a sticker. private func clearSuggestedStickersCache() { diff --git a/SignalServiceKit/src/Storage/FullTextSearchFinder.swift b/SignalServiceKit/src/Storage/FullTextSearchFinder.swift index aeb613781a..ada4b5bd98 100644 --- a/SignalServiceKit/src/Storage/FullTextSearchFinder.swift +++ b/SignalServiceKit/src/Storage/FullTextSearchFinder.swift @@ -214,7 +214,7 @@ class GRDBFullTextSearchFinder: NSObject { private static let serialQueue = DispatchQueue(label: "org.signal.fts") // This should only be accessed on serialQueue. - private static let ftsCache: NSCache = NSCache() + private static let ftsCache = NSCache(countLimit: 128) private class func cacheKey(collection: String, uniqueId: String) -> String { return "\(collection).\(uniqueId)" diff --git a/SignalServiceKit/src/Util/LRUCache.swift b/SignalServiceKit/src/Util/LRUCache.swift index 86d85d97ee..4616808f3e 100644 --- a/SignalServiceKit/src/Util/LRUCache.swift +++ b/SignalServiceKit/src/Util/LRUCache.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. +// Copyright (c) 2021 Open Whisper Systems. All rights reserved. // @objc @@ -28,6 +28,8 @@ public class AnyLRUCache: NSObject { } } +// MARK: - + // A simple LRU cache bounded by the number of entries. public class LRUCache { @@ -103,3 +105,16 @@ public class LRUCache { cacheOrder.removeAll() } } + +// MARK: - + +@objc +public extension NSCache { + @objc(initWithCountLimit:) + public convenience init(countLimit: Int) { + self.init() + + // TODO: We might set count limit to zero in NSE? + self.countLimit = countLimit + } +} diff --git a/SignalServiceKit/src/Util/ModelReadCache.swift b/SignalServiceKit/src/Util/ModelReadCache.swift index a72d23999e..9eba9033a0 100644 --- a/SignalServiceKit/src/Util/ModelReadCache.swift +++ b/SignalServiceKit/src/Util/ModelReadCache.swift @@ -84,8 +84,11 @@ private class ModelCacheAdapter>() + fileprivate let nsCache: NSCache> private let adapter: ModelCacheAdapter @@ -151,6 +154,7 @@ private class ModelReadCache) { self.mode = mode self.adapter = adapter + self.nsCache = NSCache(countLimit: adapter.cacheCountLimit) switch mode { case .read: @@ -649,7 +653,7 @@ public class UserProfileReadCache: NSObject { } private let cache: ModelReadCacheWrapper - private let adapter = Adapter(cacheName: "UserProfile") + private let adapter = Adapter(cacheName: "UserProfile", cacheCountLimit: 32) @objc public override init() { @@ -718,7 +722,7 @@ public class SignalAccountReadCache: NSObject { } private let cache: ModelReadCacheWrapper - private let adapter = Adapter(cacheName: "SignalAccount") + private let adapter = Adapter(cacheName: "SignalAccount", cacheCountLimit: 32) @objc public override init() { @@ -786,7 +790,7 @@ public class SignalRecipientReadCache: NSObject { } private let cache: ModelReadCacheWrapper - private let adapter = Adapter(cacheName: "SignalRecipient") + private let adapter = Adapter(cacheName: "SignalRecipient", cacheCountLimit: 32) @objc public override init() { @@ -851,7 +855,7 @@ public class ThreadReadCache: NSObject { } private let cache: ModelReadCacheWrapper - private let adapter = Adapter(cacheName: "TSThread") + private let adapter = Adapter(cacheName: "TSThread", cacheCountLimit: 32) @objc public override init() { @@ -925,7 +929,7 @@ public class InteractionReadCache: NSObject { } private let cache: ModelReadCacheWrapper - private let adapter = Adapter(cacheName: "TSInteraction") + private let adapter = Adapter(cacheName: "TSInteraction", cacheCountLimit: 256) @objc public override init() { @@ -1003,7 +1007,7 @@ public class AttachmentReadCache: NSObject { } private let cache: ModelReadCacheWrapper - private let adapter = Adapter(cacheName: "TSAttachment") + private let adapter = Adapter(cacheName: "TSAttachment", cacheCountLimit: 256) @objc public override init() { @@ -1067,7 +1071,7 @@ public class InstalledStickerCache: NSObject { } private let cache: ModelReadCacheWrapper - private let adapter = Adapter(cacheName: "InstalledSticker") + private let adapter = Adapter(cacheName: "InstalledSticker", cacheCountLimit: 32) @objc public override init() {