Skip to content

Commit c4b5e84

Browse files
committed
update
efficient thread safety
1 parent 3ff4c0f commit c4b5e84

File tree

3 files changed

+107
-136
lines changed

3 files changed

+107
-136
lines changed

Sources/PureMVC/core/Controller.swift

Lines changed: 43 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -38,38 +38,38 @@ registrations.
3838
`@see org.puremvc.swift.multicore.patterns.command.MacroCommand MacroCommand`
3939
*/
4040
open class Controller: IController {
41-
41+
4242
/// Local reference to View
4343
internal var view: IView!
44-
44+
4545
// Mapping of Notification names to factories that instanties and returns `ICommand` Class instances
4646
internal var commandMap = [String: () -> ICommand]()
47-
47+
4848
// Concurrent queue for commandMap
4949
// for speed and convenience of running concurrently while reading, and thread safety of blocking while mutating
5050
internal let commandMapQueue = DispatchQueue(label: "org.puremvc.controller.commandMapQueue", attributes: DispatchQueue.Attributes.concurrent)
51-
51+
5252
/// The Multiton Key for this app
5353
internal private(set) var multitonKey: String
54-
54+
5555
// The Multiton Controller instanceMap.
5656
private static var instanceMap = [String: IController]()
57-
57+
5858
// instance Queue for thread safety
5959
private static let instanceQueue = DispatchQueue(label: "org.puremvc.controller.instanceQueue", attributes: DispatchQueue.Attributes.concurrent)
60-
60+
6161
/// Message constant
6262
internal static let MULTITON_MSG = "Controller instance for this Multiton key already constructed!"
63-
63+
6464
/**
6565
Constructor.
66-
66+
6767
This `IController` implementation is a Multiton,
6868
so you should not call the constructor
6969
directly, but instead call the static Factory method,
7070
passing the unique key for this instance
7171
`Controller.getInstance( multitonKey )`
72-
72+
7373
@throws Error if instance for this Multiton key has already been constructed
7474
*/
7575
public init(key: String) {
@@ -78,17 +78,17 @@ open class Controller: IController {
7878
Controller.instanceMap[key] = self
7979
initializeController()
8080
}
81-
81+
8282
/**
8383
Initialize the Multiton `Controller` instance.
84-
84+
8585
Called automatically by the constructor.
86-
86+
8787
Note that if you are using a subclass of `View`
8888
in your application, you should *also* subclass `Controller`
8989
and override the `initializeController` method in the
9090
following way:
91-
91+
9292
// ensure that the Controller is talking to my IView implementation
9393
override public func initializeController( ) {
9494
view = MyView.getInstance(multitonKey) { MyView(key: self.multitonKey) }
@@ -97,10 +97,10 @@ open class Controller: IController {
9797
open func initializeController() {
9898
view = View.getInstance(multitonKey) { key in View(key: key) }
9999
}
100-
100+
101101
/**
102102
`Controller` Multiton Factory method.
103-
103+
104104
- parameter key: multitonKey
105105
- parameter factory: reference that returns `IController`
106106
- returns: the Multiton instance
@@ -113,18 +113,18 @@ open class Controller: IController {
113113
}
114114
return instanceMap[key]!
115115
}
116-
116+
117117
/**
118118
Register a particular `ICommand` class as the handler
119119
for a particular `INotification`.
120-
120+
121121
If an `ICommand` has already been registered to
122122
handle `INotification`s with this name, it is no longer
123123
used, the new `ICommand` is used instead.
124-
124+
125125
The Observer for the new ICommand is only created if this the
126126
first time an ICommand has been regisered for this Notification name.
127-
127+
128128
- parameter notificationName: the name of the `INotification`
129129
- parameter factory: reference that returns `ICommand`
130130
*/
@@ -136,60 +136,59 @@ open class Controller: IController {
136136
commandMap[notificationName] = factory
137137
}
138138
}
139-
139+
140140
/**
141141
If an `ICommand` has previously been registered
142142
to handle a the given `INotification`, then it is executed.
143-
144-
- parameter note: an `INotification`
143+
144+
- parameter notification: an `INotification`
145145
*/
146146
open func executeCommand(_ notification: INotification) {
147-
commandMapQueue.sync {
148-
if let factory = commandMap[notification.name] {
149-
let commandInstance = factory()
150-
commandInstance.initializeNotifier(multitonKey)
151-
commandInstance.execute(notification)
152-
}
147+
let factory = commandMapQueue.sync {
148+
commandMap[notification.name]
153149
}
150+
151+
guard let command = factory?() else { return }
152+
153+
command.initializeNotifier(multitonKey)
154+
command.execute(notification)
154155
}
155-
156+
156157
/**
157158
Check if a Command is registered for a given Notification
158-
159+
159160
- parameter notificationName:
160161
- returns: whether a Command is currently registered for the given `notificationName`.
161162
*/
162163
open func hasCommand(_ notificationName: String) -> Bool {
163-
var result = false
164164
commandMapQueue.sync {
165-
result = commandMap[notificationName] != nil
165+
commandMap[notificationName] != nil
166166
}
167-
return result
168167
}
169-
168+
170169
/**
171170
Remove a previously registered `ICommand` to `INotification` mapping.
172-
171+
173172
- parameter notificationName: the name of the `INotification` to remove the `ICommand` mapping for
174173
*/
175174
open func removeCommand(_ notificationName: String) {
176-
if hasCommand(notificationName) {
177-
commandMapQueue.sync(flags: .barrier) {
178-
view.removeObserver(notificationName, notifyContext: self)
179-
commandMap.removeValue(forKey: notificationName)
180-
}
175+
commandMapQueue.sync(flags: .barrier) {
176+
guard commandMap[notificationName] != nil else { return }
177+
view.removeObserver(notificationName, notifyContext: self)
178+
commandMap.removeValue(forKey: notificationName)
181179
}
182-
}
183180

181+
}
182+
184183
/**
185184
Remove an IController instance
186-
185+
187186
- parameter multitonKey: of IController instance to remove
188187
*/
189188
open class func removeController(_ key: String) {
190189
instanceQueue.sync(flags: .barrier) {
191190
_ = instanceMap.removeValue(forKey: key)
192191
}
193192
}
194-
193+
195194
}

Sources/PureMVC/core/Model.swift

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,11 @@ open class Model: IModel {
103103
- parameter proxy: an `IProxy` to be held by the `Model`.
104104
*/
105105
open func registerProxy(_ proxy: IProxy) {
106+
proxy.initializeNotifier(multitonKey)
106107
proxyMapQueue.sync(flags: .barrier) {
107-
proxy.initializeNotifier(multitonKey)
108108
proxyMap[proxy.name] = proxy
109-
proxy.onRegister()
110109
}
110+
proxy.onRegister()
111111
}
112112

113113
/**
@@ -117,11 +117,9 @@ open class Model: IModel {
117117
- returns: the `IProxy` instance previously registered with the given `proxyName`.
118118
*/
119119
open func retrieveProxy(_ proxyName: String) -> IProxy? {
120-
var proxy: IProxy?
121120
proxyMapQueue.sync {
122-
proxy = proxyMap[proxyName]
121+
proxyMap[proxyName]
123122
}
124-
return proxy
125123
}
126124

127125
/**
@@ -131,11 +129,9 @@ open class Model: IModel {
131129
- returns: whether a Proxy is currently registered with the given `proxyName`.
132130
*/
133131
open func hasProxy(_ proxyName: String) -> Bool {
134-
var result = false
135132
proxyMapQueue.sync {
136-
result = proxyMap[proxyName] != nil
133+
proxyMap[proxyName] != nil
137134
}
138-
return result
139135
}
140136

141137
/**
@@ -147,11 +143,10 @@ open class Model: IModel {
147143
@discardableResult open func removeProxy(_ proxyName: String) -> IProxy? {
148144
var removed: IProxy?
149145
proxyMapQueue.sync(flags: .barrier) {
150-
if let proxy = proxyMap[proxyName] {
151-
proxy.onRemove()
152-
removed = proxyMap.removeValue(forKey: proxyName)
153-
}
146+
removed = proxyMap.removeValue(forKey: proxyName)
154147
}
148+
149+
removed?.onRemove()
155150
return removed
156151
}
157152

0 commit comments

Comments
 (0)