Skip to content

Commit c2023c7

Browse files
authored
Move NotificationCenter to swift-foundation (#5246)
1 parent 9529fb3 commit c2023c7

File tree

2 files changed

+42
-95
lines changed

2 files changed

+42
-95
lines changed

Sources/Foundation/NSNotification.swift

Lines changed: 42 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
88
//
99

10+
@_spi(SwiftCorelibsFoundation) import FoundationEssentials
11+
1012
@available(*, unavailable)
1113
extension NSNotification : @unchecked Sendable { }
1214

@@ -87,116 +89,67 @@ open class NSNotification: NSObject, NSCopying, NSCoding {
8789

8890
#if canImport(Dispatch)
8991

90-
private class NSNotificationReceiver : NSObject {
91-
fileprivate var name: Notification.Name?
92-
fileprivate var block: (@Sendable (Notification) -> Void)?
93-
fileprivate var sender: AnyObject?
94-
fileprivate var queue: OperationQueue?
95-
}
96-
97-
private let _defaultCenter: NotificationCenter = NotificationCenter()
98-
99-
open class NotificationCenter: NSObject, @unchecked Sendable {
100-
private lazy var _nilIdentifier: ObjectIdentifier = ObjectIdentifier(_observersLock)
101-
private lazy var _nilHashable: AnyHashable = AnyHashable(_nilIdentifier)
102-
103-
private var _observers: [AnyHashable /* Notification.Name */ : [ObjectIdentifier /* object */ : [ObjectIdentifier /* notification receiver */ : NSNotificationReceiver]]]
104-
private let _observersLock = NSLock()
105-
106-
public required override init() {
107-
_observers = [AnyHashable: [ObjectIdentifier: [ObjectIdentifier: NSNotificationReceiver]]]()
92+
extension NotificationCenter {
93+
public func post(_ notification: Notification) {
94+
_post(notification.name.rawValue, subject: notification.object, message: notification)
10895
}
109-
110-
open class var `default`: NotificationCenter {
111-
return _defaultCenter
112-
}
113-
114-
open func post(_ notification: Notification) {
115-
let notificationNameIdentifier: AnyHashable = AnyHashable(notification.name)
116-
let senderIdentifier: ObjectIdentifier? = notification.object.map({ ObjectIdentifier(__SwiftValue.store($0)) })
117-
11896

119-
let sendTo: [Dictionary<ObjectIdentifier, NSNotificationReceiver>.Values] = _observersLock.synchronized({
120-
var retVal = [Dictionary<ObjectIdentifier, NSNotificationReceiver>.Values]()
121-
(_observers[_nilHashable]?[_nilIdentifier]?.values).map({ retVal.append($0) })
122-
senderIdentifier.flatMap({ _observers[_nilHashable]?[$0]?.values }).map({ retVal.append($0) })
123-
(_observers[notificationNameIdentifier]?[_nilIdentifier]?.values).map({ retVal.append($0) })
124-
senderIdentifier.flatMap({ _observers[notificationNameIdentifier]?[$0]?.values}).map({ retVal.append($0) })
125-
126-
return retVal
127-
})
128-
129-
sendTo.forEach { observers in
130-
observers.forEach { observer in
131-
guard let block = observer.block else {
132-
return
133-
}
134-
135-
if let queue = observer.queue, queue != OperationQueue.current {
136-
// Not entirely safe, but maintained for compatibility
137-
nonisolated(unsafe) let unsafeNotification = notification
138-
queue.addOperation { block(unsafeNotification) }
139-
queue.waitUntilAllOperationsAreFinished()
140-
} else {
141-
block(notification)
142-
}
143-
}
144-
}
145-
}
146-
147-
open func post(name aName: NSNotification.Name, object anObject: Any?, userInfo aUserInfo: [AnyHashable : Any]? = nil) {
148-
let notification = Notification(name: aName, object: anObject, userInfo: aUserInfo)
149-
post(notification)
97+
public func post(name aName: NSNotification.Name, object anObject: Any?, userInfo aUserInfo: [AnyHashable : Any]? = nil) {
98+
post(Notification(name: aName, object: anObject, userInfo: aUserInfo))
15099
}
151100

152-
open func removeObserver(_ observer: Any) {
101+
public func removeObserver(_ observer: Any) {
153102
removeObserver(observer, name: nil, object: nil)
154103
}
155104

156-
open func removeObserver(_ observer: Any, name aName: NSNotification.Name?, object: Any?) {
157-
guard let observer = observer as? NSNotificationReceiver,
105+
public func removeObserver(_ observer: Any, name aName: NSNotification.Name?, object: Any?) {
106+
let objectId = object != nil ? object.map { ObjectIdentifier($0 as AnyObject) } : nil
107+
108+
guard let observer = observer as? _NSNotificationObserverToken,
158109
// These 2 parameters would only be useful for removing notifications added by `addObserver:selector:name:object:`
159-
aName == nil || observer.name == aName,
160-
object == nil || observer.sender === __SwiftValue.store(object)
110+
aName == nil || observer.token.name == aName?.rawValue,
111+
objectId == nil || observer.token.objectId == objectId
161112
else {
162113
return
163114
}
164-
165-
let notificationNameIdentifier: AnyHashable = observer.name.map { AnyHashable($0) } ?? _nilHashable
166-
let senderIdentifier: ObjectIdentifier = observer.sender.map { ObjectIdentifier($0) } ?? _nilIdentifier
167-
let receiverIdentifier: ObjectIdentifier = ObjectIdentifier(observer)
168115

169-
_observersLock.synchronized({
170-
_observers[notificationNameIdentifier]?[senderIdentifier]?.removeValue(forKey: receiverIdentifier)
171-
if _observers[notificationNameIdentifier]?[senderIdentifier]?.count == 0 {
172-
_observers[notificationNameIdentifier]?.removeValue(forKey: senderIdentifier)
173-
}
174-
})
116+
_removeObserver(observer.token)
175117
}
176118

177119
@available(*, unavailable, renamed: "addObserver(forName:object:queue:using:)")
178-
open func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, usingBlock block: @escaping (Notification) -> Void) -> NSObjectProtocol {
120+
public func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, usingBlock block: @escaping (Notification) -> Void) -> NSObjectProtocol {
179121
fatalError()
180122
}
181123

182-
open func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, using block: @Sendable @escaping (Notification) -> Void) -> NSObjectProtocol {
183-
let newObserver = NSNotificationReceiver()
184-
newObserver.name = name
185-
newObserver.block = block
186-
newObserver.sender = __SwiftValue.store(obj)
187-
newObserver.queue = queue
188-
189-
let notificationNameIdentifier: AnyHashable = name.map({ AnyHashable($0) }) ?? _nilHashable
190-
let senderIdentifier: ObjectIdentifier = newObserver.sender.map({ ObjectIdentifier($0) }) ?? _nilIdentifier
191-
let receiverIdentifier: ObjectIdentifier = ObjectIdentifier(newObserver)
124+
public func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, using block: @Sendable @escaping (Notification) -> Void) -> NSObjectProtocol {
125+
if let queue = queue {
126+
return _NSNotificationObserverToken(token: _addObserver(name?.rawValue, object: obj) { [weak queue] (notification: Notification) in
127+
if let queue = queue {
128+
if queue != OperationQueue.current {
129+
// Not entirely safe, but maintained for compatibility
130+
nonisolated(unsafe) let notification = notification
131+
queue.addOperation { block(notification) }
132+
queue.waitUntilAllOperationsAreFinished()
133+
} else {
134+
block(notification)
135+
}
136+
}
137+
})
138+
} else {
139+
return _NSNotificationObserverToken(token: _addObserver(name?.rawValue, object: obj, using: block))
140+
}
141+
}
142+
}
192143

193-
_observersLock.synchronized({
194-
_observers[notificationNameIdentifier, default: [:]][senderIdentifier, default: [:]][receiverIdentifier] = newObserver
195-
})
144+
extension NotificationCenter {
145+
// Provides NSObjectProtocol conformance for addObserver()
146+
final class _NSNotificationObserverToken: NSObject {
147+
internal let token: NotificationCenter._NotificationObserverToken
196148

197-
return newObserver
149+
init(token: NotificationCenter._NotificationObserverToken) {
150+
self.token = token
151+
}
198152
}
199-
200153
}
201154

202155
#endif // canImport(Dispatch)

Tests/Foundation/TestNotificationCenter.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@
1010
final class NotificationCenterDummyObject : NSObject, Sendable { }
1111

1212
class TestNotificationCenter : XCTestCase {
13-
func test_defaultCenter() {
14-
let defaultCenter1 = NotificationCenter.default
15-
let defaultCenter2 = NotificationCenter.default
16-
XCTAssertEqual(defaultCenter1, defaultCenter2)
17-
}
18-
1913
func removeObserver(_ observer: NSObjectProtocol, notificationCenter: NotificationCenter) {
2014
guard let observer = observer as? NSObject else {
2115
return

0 commit comments

Comments
 (0)