Skip to content

Commit 0529a7a

Browse files
committed
Rework ObjectAssociation into ObjectStorage
1 parent d30e9e9 commit 0529a7a

File tree

4 files changed

+80
-56
lines changed

4 files changed

+80
-56
lines changed

Ice/ControlItem/ControlItem.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ final class ControlItem {
467467
keyEquivalent: ""
468468
)
469469
item.target = self
470-
Self.sectionStorage[item] = section
470+
Self.sectionStorage.weakSet(section, for: item)
471471
switch name {
472472
case .visible:
473473
break
@@ -516,7 +516,7 @@ final class ControlItem {
516516

517517
/// Toggles the menu bar section associated with the given menu item.
518518
@objc private func toggleMenuBarSection(for menuItem: NSMenuItem) {
519-
Self.sectionStorage[menuItem]?.toggle()
519+
Self.sectionStorage.value(for: menuItem)?.toggle()
520520
}
521521

522522
/// Opens the menu bar search panel.
@@ -567,7 +567,7 @@ private extension ControlItem {
567567
///
568568
/// When one of these menu items is created, its section is stored here.
569569
/// When its action is invoked, the section is retrieved from storage.
570-
static let sectionStorage = ObjectAssociation<MenuBarSection>()
570+
static let sectionStorage = ObjectStorage<MenuBarSection>()
571571
}
572572

573573
// MARK: - Logger

Ice/Main/AppState.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ final class AppState: ObservableObject {
251251
// Store whether the app has previously activated inside an internal
252252
// context to keep it isolated.
253253
enum Context {
254-
static let hasActivated = ObjectAssociation<Bool>()
254+
static let hasActivated = ObjectStorage<Bool>()
255255
}
256256

257257
func activate() {
@@ -263,10 +263,10 @@ final class AppState: ObservableObject {
263263
NSApp.setActivationPolicy(policy)
264264
}
265265

266-
if Context.hasActivated[self] == true {
266+
if Context.hasActivated.value(for: self) == true {
267267
activate()
268268
} else {
269-
Context.hasActivated[self] = true
269+
Context.hasActivated.set(true, for: self)
270270
Logger.appState.debug("First time activating app, so going through Dock")
271271
// Hack to make sure the app properly activates for the first time.
272272
NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.dock").first?.activate()

Ice/Utilities/ObjectAssociation.swift

Lines changed: 0 additions & 50 deletions
This file was deleted.

Ice/Utilities/ObjectStorage.swift

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// ObjectStorage.swift
3+
// Ice
4+
//
5+
6+
import ObjectiveC
7+
8+
// MARK: - Object Storage
9+
10+
/// A type that uses the Objective-C runtime to store values of a given
11+
/// type with an object.
12+
final class ObjectStorage<Value> {
13+
/// The association policy to use for storage.
14+
///
15+
/// - Note: Regardless of whether a value is stored with a strong or
16+
/// weak reference, the association is made strongly. Weak references
17+
/// are stored inside a `WeakReference` object.
18+
private let policy = objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
19+
20+
/// The key used for value lookup.
21+
///
22+
/// The key is unique to this instance.
23+
private var key: UnsafeRawPointer {
24+
UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())
25+
}
26+
27+
/// Sets the value for the given object.
28+
///
29+
/// If the value is an object, it is stored with a strong reference.
30+
/// Use ``weakSet(_:for:)`` to store an object with a weak reference.
31+
///
32+
/// - Parameters:
33+
/// - value: A value to set.
34+
/// - object: An object to set the value for.
35+
func set(_ value: Value?, for object: AnyObject) {
36+
objc_setAssociatedObject(object, key, value, policy)
37+
}
38+
39+
/// Retrieves the value stored for the given object.
40+
///
41+
/// - Parameter object: An object to retrieve the value for.
42+
func value(for object: AnyObject) -> Value? {
43+
let value = objc_getAssociatedObject(object, key)
44+
return if let container = value as? WeakReference {
45+
container.object as? Value
46+
} else {
47+
value as? Value
48+
}
49+
}
50+
}
51+
52+
// MARK: - Weak Storage
53+
54+
/// An object containing a weak reference to another object.
55+
private final class WeakReference {
56+
/// A weak reference to an object.
57+
private(set) weak var object: AnyObject?
58+
59+
/// Creates a weak reference to an object.
60+
init(_ object: AnyObject) {
61+
self.object = object
62+
}
63+
}
64+
65+
extension ObjectStorage where Value: AnyObject {
66+
/// Sets a weak reference to an object.
67+
///
68+
/// - Parameters:
69+
/// - value: An object to set a weak reference to.
70+
/// - object: An object to set the weak reference for.
71+
func weakSet(_ value: Value?, for object: AnyObject) {
72+
objc_setAssociatedObject(object, key, value.map(WeakReference.init), policy)
73+
}
74+
}

0 commit comments

Comments
 (0)