Skip to content

Commit

Permalink
Remove extra containers after resize
Browse files Browse the repository at this point in the history
  • Loading branch information
ApolloZhu committed Aug 20, 2019
1 parent d304ba9 commit b2102e8
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 12 deletions.
49 changes: 38 additions & 11 deletions Sources/EFStorageCore/EFSingleInstanceStorageReference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,38 @@ public protocol EFSingleInstanceStorageReference: AnyObject, EFOptionalContentWr

import Foundation

var efStorages = [String: NSMapTable<NSString, AnyObject>]()
var efStoragesLock = NSLock()
internal enum _EFStorages {
internal typealias Table = [String: NSMapTable<NSString, AnyObject>]

private static var _efStorages = Table() {
didSet {
if oldValue.capacity != _efStorages.capacity {
modify(by: organize)
}
}
}

/// `organize` happens when `lock` is obtained, so it has to be recursive
private static var lock = NSRecursiveLock()

private func organizeEFStorages() {
#warning("需要找一个时机调用来清理不需要的容器")
#warning("Needs performance test once integrated")
efStoragesLock.lock()
defer { efStoragesLock.unlock() }
// http://cocoamine.net/blog/2013/12/13/nsmaptable-and-zeroing-weak-references/
efStorages = efStorages.filter { $0.value.keyEnumerator().allObjects.count == 0 }
internal static func modify<T>(by mutate: (inout Table) throws -> T) rethrows -> T {
lock.lock()
defer { lock.unlock() }
return try mutate(&_efStorages)
}

internal static func read<T>(by access: (Table) throws -> T) rethrows -> T {
lock.lock()
defer { lock.unlock() }
return try access(_efStorages)
}

private static func organize(_ efStorages: inout Table) {
_efStorageLog("CLEAN START \(efStorages.count)")
// http://cocoamine.net/blog/2013/12/13/nsmaptable-and-zeroing-weak-references/
efStorages = efStorages.filter { !$0.value.keyEnumerator().allObjects.isEmpty }
_efStorageLog("CLEAN AFTER \(efStorages.count)")
}
}

@inlinable
Expand All @@ -47,8 +69,13 @@ extension EFSingleInstanceStorageReference {
}

public static func forKey(_ key: String, in storage: Storage = Storage.makeDefault()) -> Self {
efStoragesLock.lock()
defer { efStoragesLock.unlock() }
return _EFStorages.modify { efStorages in
return make(forKey: key, in: storage, efStorages: &efStorages)
}
}

private static func make(forKey key: String, in storage: Storage,
efStorages: inout _EFStorages.Table) -> Self {
let typeIdentifier = String(describing: self)
if efStorages[typeIdentifier] == nil {
_efStorageLog("ALLOC \(typeIdentifier)")
Expand Down
18 changes: 17 additions & 1 deletion Tests/EFStorageTests/EFStorageTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,23 @@ final class EFStorageTests: XCTestCase {

var storageText: EFStorageUserDefaultsRef<String> = UserDefaults.efStorage.text

private func printValue<T: CustomStringConvertible>(_ t: T?, ofType type: T.Type,
or defaultValue: @autoclosure () -> String) {
print("VALUE \(t?.description ?? defaultValue())")
}

func testExample() {
printValue(UserDefaults.efStorage.nonExisting, ofType: CGFloat.self, or: "SHOULD DISAPPEAR")
printValue(UserDefaults.efStorage.nonExisting, ofType: Data.self, or: "SHOULD DISAPPEAR")
printValue(UserDefaults.efStorage.nonExisting, ofType: NSArray.self, or: "SHOULD DISAPPEAR")
printValue(UserDefaults.efStorage.nonExisting, ofType: Int8.self, or: "SHOULD DISAPPEAR")
printValue(UserDefaults.efStorage.nonExisting, ofType: Int16.self, or: "SHOULD DISAPPEAR")
printValue(UserDefaults.efStorage.nonExisting, ofType: Int32.self, or: "SHOULD DISAPPEAR")
printValue(UserDefaults.efStorage.nonExisting, ofType: Int64.self, or: "SHOULD DISAPPEAR")
printValue(UserDefaults.efStorage.nonExisting, ofType: UInt8.self, or: "SHOULD DISAPPEAR")
printValue(UserDefaults.efStorage.nonExisting, ofType: UInt16.self, or: "SHOULD DISAPPEAR")
printValue(UserDefaults.efStorage.nonExisting, ofType: UInt32.self, or: "SHOULD DISAPPEAR")
printValue(UserDefaults.efStorage.nonExisting, ofType: UInt64.self, or: "SHOULD DISAPPEAR")
XCTAssertEqual(text, EFStorageTests.defaultText)
text = "meow"
XCTAssertEqual(_text.wrappedValue, "meow")
Expand All @@ -70,7 +86,7 @@ final class EFStorageTests: XCTestCase {
let hasPaidBeforeRef: EFStorageUserDefaultsRef<Bool> = UserDefaults.efStorage.oldHasPaidBeforeKey
XCTAssertEqual(hasPaidBeforeRef.content, true)
XCTAssertEqual(UserDefaults.standard.bool(forKey: "oldHasPaidBeforeKey"), true)
print(efStorages)
_EFStorages.read { print($0) }
XCTAssertEqual(hasPaidBefore, true)
XCTAssertEqual(mixedType, "1551")
mixedType = "Brand New"
Expand Down

0 comments on commit b2102e8

Please sign in to comment.