How to Synchronize Access to a Property That Has Didset

What is the purpose of willSet and didSet in Swift?

The point seems to be that sometimes, you need a property that has automatic storage and some behavior, for instance to notify other objects that the property just changed. When all you have is get/set, you need another field to hold the value. With willSet and didSet, you can take action when the value is modified without needing another field. For instance, in that example:

class Foo {
var myProperty: Int = 0 {
didSet {
print("The value of myProperty changed from \(oldValue) to \(myProperty)")
}
}
}

myProperty prints its old and new value every time it is modified. With just getters and setters, I would need this instead:

class Foo {
var myPropertyValue: Int = 0
var myProperty: Int {
get { return myPropertyValue }
set {
print("The value of myProperty changed from \(myPropertyValue) to \(newValue)")
myPropertyValue = newValue
}
}
}

So willSet and didSet represent an economy of a couple of lines, and less noise in the field list.

Accessing actor properties synchronously from task-less context

I found a way to do it by using Combine, like this:

import Combine

actor Foo {

nonisolated let valuePublisher = CurrentValueSubject<Int, Never>(0)

var value: Int = 0 {
didSet {
valuePublisher.value = value
}
}
}

By providing an non-isolated publisher, we can propagate the actor value safely to the outside, since Publishers are thread safe.

Callers can either access foo.valuePublisher.value or subscribe to it from the outside, without requiring an async context.

Property Observers on NSUserDefaults

The easiest way to do that is to combine both user defaults and animations on a computed variable as follow:

var defaultName:String{
get {

var returnValue: NSString? = NSUserDefaults.standardUserDefaults().objectForKey("defaultName") as? NSString

if returnValue == nil //Check for first run of app
{
returnValue = ""
}
return returnValue!
}

set (newValue) {
NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: "defaultName")
NSUserDefaults.standardUserDefaults().synchronize()
// start your animations here
}
}

Next on button click just change the defaultName value so both newValue will be stored and animations will trigger

Are Swift variables atomic?

It's very early to assume as no low-level documentation is available, but you can study from assembly. Hopper Disassembler is a great tool.

@interface ObjectiveCar : NSObject
@property (nonatomic, strong) id engine;
@property (atomic, strong) id driver;
@end

Uses objc_storeStrong and objc_setProperty_atomic for nonatomic and atomic respectively, where

class SwiftCar {
var engine : AnyObject?
init() {
}
}

uses swift_retain from libswift_stdlib_core and, apparently, does not have thread safety built in.

We can speculate that additional keywords (similar to @lazy) might be introduced later on.

Update 07/20/15: according to this blogpost on singletons swift environment can make certain cases thread safe for you, i.e.:

class Car {
static let sharedCar: Car = Car() // will be called inside of dispatch_once
}

private let sharedCar: Car2 = Car2() // same here
class Car2 {

}

Update 05/25/16: Keep an eye out for swift evolution proposal https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md - it looks like it is going to be possible to have @atomic behavior implemented by yourself.

Thread safe singleton in swift

Thanks to @rmaddy comments which pointed me in the right direction I was able to solve the problem.

In order to make the property foo of the Singleton thread safe, it need to be modified as follows:

    class Singleton {

static let shared = Singleton()

private init(){}

private let internalQueue = DispatchQueue(label: "com.singletioninternal.queue",
qos: .default,
attributes: .concurrent)

private var _foo: String = "aaa"

var foo: String {
get {
return internalQueue.sync {
_foo
}
}
set (newState) {
internalQueue.async(flags: .barrier) {
self._foo = newState
}
}
}

func setup(string: String) {
foo = string
}
}

Thread safety is accomplished by having a computed property foo which uses an internalQueue to access the "real" _foo property.

Also, in order to have better performance internalQueue is created as concurrent. And it means that it is needed to add the barrier flag when writing to the property.

What the barrier flag does is to ensure that the work item will be executed when all previously scheduled work items on the queue have finished.



Related Topics



Leave a reply



Submit