22 MulticastDelegate
33 B9Swift
44
5- Copyright © 2019 BB9z
5+ Copyright © 2019-2021 BB9z
66 https://github.com/B9Swift/MulticastDelegate
77
88 The MIT License (MIT)
1212import Foundation
1313
1414/// Multicast delegate is a delegate that can have more than one element in its invocation list.
15+ ///
16+ /// This class is thread safe.
1517public final class MulticastDelegate < Element> {
1618
1719 public init ( ) {
1820 }
1921
20- private lazy var store = [ Weak] ( )
22+ private var store = [ Weak] ( )
23+ private let lock = NSLock ( )
2124
2225 /// Add an object to the multicast delegate.
2326 ///
@@ -31,10 +34,14 @@ public final class MulticastDelegate<Element> {
3134 guard let d = delegate else { return }
3235 let weakRef = Weak ( object: d as AnyObject )
3336 guard let dobj = weakRef. object else {
34- print ( " [B9MulticastDelegate] warning: \( d) is not an object, it will be ignored. Adding a non-object to the delegate is meaningless. " )
37+ print ( " [B9MulticastDelegate] warning: \( d) is not an object, it will be ignored. Adding a non-object as delegate is meaningless. " )
38+ return
39+ }
40+ lock. lock ( )
41+ defer { lock. unlock ( ) }
42+ if store. contains ( where: { $0. object === dobj } ) {
3543 return
3644 }
37- if store. contains ( where: { $0. object === dobj } ) { return }
3845 store. append ( weakRef)
3946 underestimatedCount += 1
4047 }
@@ -46,8 +53,10 @@ public final class MulticastDelegate<Element> {
4653 /// - Complexity: O(*n*), where *n* is the length of the internal storage.
4754 public func remove( _ delegate: Element ? ) {
4855 guard let d = delegate else { return }
56+ lock. lock ( )
4957 store. removeAll { $0. object === d as AnyObject || $0. object == nil }
5058 underestimatedCount = store. count
59+ lock. unlock ( )
5160 }
5261
5362 /// Calls the given closure on each object in the multicast delegate.
@@ -56,7 +65,10 @@ public final class MulticastDelegate<Element> {
5665 ///
5766 /// - Parameter invocation: A closure that takes an object in the multicast delegate as a parameter.
5867 public func invoke( _ invocation: ( Element ) throws -> ( ) ) rethrows {
59- for ref in store {
68+ lock. lock ( )
69+ let shadowStore = store
70+ lock. unlock ( )
71+ for ref in shadowStore {
6072 if let d = ref. element {
6173 try invocation ( d)
6274 }
@@ -73,6 +85,8 @@ public final class MulticastDelegate<Element> {
7385 ///
7486 /// - Complexity: O(*n*), where *n* is the length of the internal storage.
7587 public func contains( object: AnyObject ) -> Bool {
88+ lock. lock ( )
89+ defer { lock. unlock ( ) }
7690 for weakRef in store {
7791 if weakRef. object === object {
7892 return true
@@ -115,10 +129,10 @@ extension MulticastDelegate: CustomStringConvertible {
115129 let address = Unmanaged . passUnretained ( self ) . toOpaque ( )
116130 let itemsDescriptions = map { " \t \( $0) " }
117131 if itemsDescriptions. isEmpty {
118- return " < \( aType) : \( address) . elements: []>"
132+ return " < \( aType) \( address) : elements: []>"
119133 }
120134 return """
121- < \( aType) : \( address) . elements: [
135+ < \( aType) \( address) : elements: [
122136 \( itemsDescriptions. joined ( separator: " , \n " ) )
123137 ]>
124138 """
0 commit comments