How do I synchronize access to a property that has didSet?
Asked Answered
R

2

3

How do I synchronize access of a property that uses didSet (using GCD or objc_sync_enter)?

I have a property that has a property observer. How can I use a private queue to synchronize get/set of the property

var state: State = .disconnected {
  // Q: How to sync get/set access here?
  didSet {
    // do something
  }
}
Riehl answered 17/5, 2017 at 5:5 Comment(0)
P
4

simplest way is using a serial queue

import Dispatch

struct S {
    private var i: Int = 0 {
        didSet {
            print("someone did set new value:", i)
        }
    }
    private let queue = DispatchQueue(label: "private", qos: .userInteractive) // high priority
    var value: Int {
        get {
            return queue.sync {
                return i
            }
        }
        set {
            if newValue == value {
                return
            }
            queue.sync {
                i = newValue
            }
        }
    }
}

more advanced example use concurrent reading and sync barrier for writing

import Dispatch

struct S {
    private var i: Int = 0 {
        didSet {
            print("someone did set new value:", i)
        }
    }
    private let queue = DispatchQueue(label: "private", qos: .userInteractive, attributes: .concurrent) // high priority
    var value: Int {
        get {
            return queue.sync {
                return i
            }
        }
        set {
            if newValue == value {
                return
            }
            queue.sync(flags: .barrier) {
                i = newValue
            }
        }
    }
}

in the case of class property, you can use a concurrent private queue and read from different thread concurrently and dispatch write asynchronously with a barrier

import Dispatch

class S {
    private var i: Int = 0 {
        didSet {
            print("someone did set new value:", i)
        }
    }
    private let queue = DispatchQueue(label: "private", qos: .userInteractive, attributes: .concurrent) // high priority
    var value: Int {
        get {
            return queue.sync {
                return i
            }
        }
        set {
            if newValue == value {
                return
            }
            queue.async(flags: .barrier) { [unowned self] in
                self.i = newValue
            }
        }
    }
}
Prose answered 17/5, 2017 at 11:32 Comment(4)
So to enforce sync I have to create another computed property?Riehl
@Riehl you have to "isolate" your "private" storage, so using computed property seems to be a logical step, doesn't it?Prose
@Riehl I don't know, what exactly is your usage scenario, but I strongly recommend to check this nice reading mikeash.com/pyblog/…Prose
You should not use [unowned self] with async in your writer.Ance
E
0

Alternate (using objc_sync_enter):

class MyUtil {

  class func synchronize(_ blockObj: AnyObject!, closure: () -> Void) {
    objc_sync_enter(blockObj)
    closure()
    objc_sync_exit(blockObj)
  }
  class func synchronize<T>(_ blockObj: AnyObject!, closure: () -> T) -> T {
    objc_sync_enter(blockObj)
    let retVal:T = closure()
    objc_sync_exit(blockObj)
    return retVal
  }
}

struct S {
  private var i: Int = 0 {
    didSet {
        print("someone did set new value:", i)
    }
  }
  var value: Int {
    get {
        return MyUtil.synchronize(i as AnyObject) {
            return i
        }
    }
    set {
        MyUtil.synchronize(i as AnyObject) {
          if newValue == value {
              return
          }
          i = newValue
        }
    }
}
Erastatus answered 17/5, 2017 at 13:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.