Reputation: 6790
I've created a wrapper for a dictionary, I'd like to use the didSet property observer on instances of it. I can get didSet from the private dictionary but am not sure how to define it within the wrapper class.
I would like to pass the oldValue from the Swift dictionary as well, here's the class:
public class SynchronizedDictionary<K: Hashable,V> {
private var dictionary: [K:V] = Dictionary() { didSet { } }
private let accessQueue: dispatch_queue_t
public var count: Int {
get {
var count: Int!
dispatch_sync(self.accessQueue) { () -> Void in
count = self.dictionary.count
}
return count
}
}
init() {
let qosClassUserInit = QOS_CLASS_USER_INITIATED
let newConcurrentQueueAttributes = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, qosClassUserInit, 0)
let newConcurrentQueue = dispatch_queue_create("SynchronizedDictionaryAccess", newConcurrentQueueAttributes)
self.accessQueue = newConcurrentQueue
}
public subscript(index: K) -> V? {
set {
dispatch_barrier_async(self.accessQueue) {
self.dictionary[index] = newValue
}
}
get {
var element: V?
dispatch_sync(self.accessQueue) {
element = self.dictionary[index]
}
return element
}
}
/// Removes the value for the given key and the key.
public func removeValueForKey(key: K) {
dispatch_barrier_async(self.accessQueue) { () -> Void in
if self.dictionary[key] != nil {
self.dictionary.removeValueForKey(key)
}
}
}
/// Returns the dictionary values as an array.
public func values() -> [V] {
var values = [V]()
dispatch_sync(self.accessQueue) { () -> Void in
values = Array(self.dictionary.values)
}
return values
}
public func removeAll() {
dispatch_barrier_async(self.accessQueue) { () -> Void in
self.dictionary.removeAll()
}
}
public func doesObjectExistsForKey(key: K) -> Bool {
var value: V?
dispatch_sync(self.accessQueue) { () -> Void in
value = self.dictionary[key]
}
return value != nil ? true : false
}
}
Upvotes: 0
Views: 366
Reputation: 73186
You could use a delegate: called from a property observer observing updates of the private dictionary in SynchronizedDictionary
, in turn triggering a callback to some method in the class where you work with a SynchronizedDictionary
object. Below follows an example of this procedure applied to a simplified version of your custom dictionary class.
Note that the property observer (I'm using willSet
for convenience) is set to a tuple which in turn is used to update the private dictionary, rather than trying to observe changes in the private dictionary itself (the latter would make it tricky to make sense out of newValue
in the willSet
property observer).
public protocol MyDictionaryDelegate {
typealias MyKey
typealias MyValue
func existingDictionaryEntryWasUpdated(oldValue: MyValue, newPair: (MyKey, MyValue))
}
public class MinimalSynchronizedDictionary<K: Hashable, V: Comparable, T: MyDictionaryDelegate where T.MyKey == K, T.MyValue == V> {
private var updateDictionaryWithPair : (K, V) {
/* Note, willSet not called prior to update in initializer, so we can
use a dummy pair to begin with, OK */
willSet {
/* update existing dict. entry */
if let oldValue = dictionary[newValue.0] where oldValue != newValue.1 {
dictionary.updateValue(newValue.1, forKey: newValue.0)
delegate?.existingDictionaryEntryWasUpdated(oldValue, newPair: newValue)
}
/* new dict. entry or same value update */
else {
dictionary.updateValue(newValue.1, forKey: newValue.0)
}
}
}
private var dictionary: [K:V] = Dictionary()
var delegate: T?
init(dummyInitialDictPair: (K, V)) {
updateDictionaryWithPair = dummyInitialDictPair
}
internal func updateDictionaryWithPair(newPair newPair: (K, V)) {
updateDictionaryWithPair = newPair
}
}
Note that I've type constrained generic V
to protocol Comparable
for this example (to make use of !=
operator in the willSet
clause), which you can leave out if you don't plan on comparing the dictionary values against each other.
Now, example class conforming to MyDictionaryDelegate
, containing a MinimalSynchronizedDictionary
object and receiving delegate callbacks from the objects delegate.
class MyOtherClass : MyDictionaryDelegate {
typealias MyKey = String
typealias MyValue = Int
var syncDict : MinimalSynchronizedDictionary<MyKey, MyValue, MyOtherClass>
init(syncDict: MinimalSynchronizedDictionary<MyKey, MyValue, MyOtherClass>) {
self.syncDict = syncDict
self.syncDict.delegate = self
}
// MARK: MyDictionaryDelegate
func existingDictionaryEntryWasUpdated(oldValue: MyValue, newPair: (MyKey, MyValue)) {
print("Dictionary entry for key '\(newPair.0)' was updated from old value '\(oldValue)' to new value '\(newPair.1)'.")
}
}
let myDict = MinimalSynchronizedDictionary<String, Int, MyOtherClass>(dummyInitialDictPair: ("",0))
let myMainClass = MyOtherClass(syncDict: myDict)
myDict.updateDictionaryWithPair(newPair: ("World", 1))
myDict.updateDictionaryWithPair(newPair: ("Hello", 1))
myDict.updateDictionaryWithPair(newPair: ("World", 2))
/* Prints: Dictionary entry for key 'World' was updated
from old value '1' to new value '2'. */
myMainClass.syncDict.updateDictionaryWithPair(newPair: ("World", 2))
/* (key exists, but same value --> no update) */
myMainClass.syncDict.updateDictionaryWithPair(newPair: ("World", 3))
/* Prints: Dictionary entry for key 'World' was updated
from old value '2' to new value '3'. */
Upvotes: 2