Created
June 14, 2018 16:37
-
-
Save ncreated/5e3edaab9bf8af40016b14a9d538afde to your computer and use it in GitHub Desktop.
Container for storing many delegates and calling an operation on all at once. Uses weak references to store delegates.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
/** | |
Container for storing many delegates and calling an operation on all at once. | |
Uses weak references to store delegates. | |
*/ | |
public final class MulticastDelegate<T> { | |
private var delegates: [WeakDelegate] = [] | |
public func add(delegate: T) { | |
assert(Thread.isMainThread, "`MulticastDelegate` is not thread safe") | |
let weakified = WeakDelegate(delegate as AnyObject) | |
delegates.append(weakified) | |
} | |
public func remove(delegate: T) { | |
assert(Thread.isMainThread, "`MulticastDelegate` is not thread safe") | |
cleanDeallocatedDelegates() | |
if let index = delegates.index(where: { $0.delegate === delegate as AnyObject }){ | |
delegates.remove(at: index) | |
} | |
} | |
public func call(_ block: (T) -> ()) { | |
assert(Thread.isMainThread, "`MulticastDelegate` is not thread safe") | |
cleanDeallocatedDelegates() | |
let existingDelegates = delegates.compactMap { $0.delegate as? T } | |
existingDelegates.forEach { block($0) } | |
} | |
private func cleanDeallocatedDelegates() { | |
delegates = delegates.filter { $0.delegate !== nil } | |
} | |
} | |
private class WeakDelegate { | |
weak var delegate: AnyObject? | |
init(_ delegate: AnyObject) { | |
self.delegate = delegate | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import XCTest | |
@testable import ... | |
private protocol DispatcherDelegate: class { | |
func didDispatch() | |
} | |
private class Listener: DispatcherDelegate { | |
var didDispatch_callsCount = 0 | |
func didDispatch() { | |
didDispatch_callsCount += 1 | |
} | |
} | |
class MulticastDelegateTests: XCTestCase { | |
func testCallingSingleDelegate() { | |
let delegates = MulticastDelegate<DispatcherDelegate>() | |
let listener = Listener() | |
delegates.add(delegate: listener) | |
delegates.call { $0.didDispatch() } | |
XCTAssertEqual(listener.didDispatch_callsCount, 1) | |
} | |
func testAddingDelegates() { | |
let delegates = MulticastDelegate<DispatcherDelegate>() | |
let listener1 = Listener() | |
let listener2 = Listener() | |
let listener3 = Listener() | |
delegates.add(delegate: listener1) | |
delegates.add(delegate: listener2) | |
delegates.add(delegate: listener3) | |
delegates.call { $0.didDispatch() } | |
XCTAssertEqual(listener1.didDispatch_callsCount, 1) | |
XCTAssertEqual(listener2.didDispatch_callsCount, 1) | |
XCTAssertEqual(listener3.didDispatch_callsCount, 1) | |
} | |
func testRemovingDelegates() { | |
let delegates = MulticastDelegate<DispatcherDelegate>() | |
let listener1 = Listener() | |
let listener2 = Listener() | |
let listener3 = Listener() | |
delegates.add(delegate: listener1) | |
delegates.add(delegate: listener2) | |
delegates.add(delegate: listener3) | |
delegates.call { $0.didDispatch() } | |
delegates.remove(delegate: listener3) | |
delegates.call { $0.didDispatch() } | |
delegates.remove(delegate: listener2) | |
delegates.call { $0.didDispatch() } | |
XCTAssertEqual(listener1.didDispatch_callsCount, 3) | |
XCTAssertEqual(listener2.didDispatch_callsCount, 2) | |
XCTAssertEqual(listener3.didDispatch_callsCount, 1) | |
} | |
func testKeepingWeakReferences() { | |
let delegates = MulticastDelegate<DispatcherDelegate>() | |
let listener1 = Listener() | |
delegates.add(delegate: listener1) | |
autoreleasepool { | |
let listener2 = Listener() | |
delegates.add(delegate: listener2) | |
} | |
var numberOfCallsMade = 0 | |
delegates.call { _ in numberOfCallsMade += 1 } | |
XCTAssertEqual(numberOfCallsMade, 1) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment