NSOperation with a delay - is it async or sync? - ios

I'm creating an NSOperation that executes a closure with a delay. The operations are added into a queue, every time before a new operation is added I cancel all existing ones in the queue:
let myOp = SomeOperation { [weak self] in /* do something */ }
queue.cancelAllOperations()
queue.addOperation(myOp)
Operation Code 1
final class SomeOperation: Operation {
private let closure: () -> Void
init(closure: #escaping () -> Void) {
self.closure = closure
}
override func main() {
if isCancelled {
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: doSomething)
}
private func doSomething() {
guard isCancelled == false else {
return
}
closure()
}
}
While the above code works, the code below doesn't. In the DispatchQueue closure, self is nil:
Operation Code 2
final class SomeOperation: Operation {
private let closure: () -> Void
init(closure: #escaping () -> Void) {
self.closure = closure
}
override func main() {
if isCancelled {
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
guard let self = self else { return }
guard isCancelled == false else { return }
self.closure()
}
}
}
So I'm trying to learn a bit deeper:
In Code 2, self is nil because as soon as DispatchQueue.main.asyncAfter… is called, the main method finishes and the operation is thus released.
Code 1 works because execute: doSomething implicitly captures/retains a self, so even after asyncAfter, self is still there.
So my questions are:
In Apple's doc it says for concurrent operations I should rather be using start, asynchronous, executing, finished, etc. In my case I just need to have a delay, not actually doing anything asynchronous, should I do it in main only or should I do it as an async operation by implementing those methods Apple suggested?
Is my thinking correct that in Code 1 there's a self retained implicitly, which doesn't sound correct and can create retain cycle?
Thanks!

You asked:
In Apple's doc it says for concurrent operations I should rather be using start, asynchronous, executing, finished, etc. In my case I just need to have a delay, not actually doing anything asynchronous, should I do it in main only or should I do it as an async operation by implementing those methods Apple suggested?
First, you are doing something asynchronous. I.e., asyncAfter is asynchronous.
Second, the motivating reason behind Apple’s concurrent operation discussion is that the operation should not finish until the asynchronous task it launched also finishes. You talk about canceling the operation, but that only makes sense if the operation is still running by the time you go to cancel it. This feature, the wrapping the asynchronous task in an object while never blocking a thread, is one of the key reasons we use operations rather than just GCD. It opens the door for all sorts of elegant dependencies between asynchronous tasks (dependencies, cancelation, etc.).
Is my thinking correct that in Code 1 there's a self retained implicitly, which doesn't sound correct and can create retain cycle?
Regarding the strong reference cycle issues, let’s look at your first example. While it is prudent for the creator of the operation to use [weak self] capture list, it should not be required. Good design of the operation (or anything using asynchronously called closures) is to have it release the closure when it is no longer needed:
class SomeOperation2: Operation {
private var closure: (() -> Void)?
init(closure: #escaping () -> Void) {
self.closure = closure
}
override func main() {
if isCancelled {
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: doSomething)
}
override func cancel() {
closure = nil
super.cancel()
}
private func doSomething() {
guard !isCancelled else {
return
}
closure?()
closure = nil
}
}
It doesn’t mean that the caller shouldn’t use [weak self] capture list, only that the operation no longer requires it, and will resolve any strong reference cycles when it is done with the closure.
[Note, in the above, I omitted synchronization of the variable, to keep it simple. But you need to synchronize your access to it to ensure thread-safe design.]
But this design begs the question as to why would you would want to keep the asyncAfter scheduled, still firing even after you canceled the operation. It would be better to cancel it, by wrapping the closure in a DispatchWorkItem, which can be canceled, e.g.:
class SomeOperation: Operation {
private var item: DispatchWorkItem!
init(closure: #escaping () -> Void) {
super.init()
item = DispatchWorkItem { [weak self] in
closure()
self?.item = nil
}
}
override func main() {
if isCancelled { return }
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: item)
}
override func cancel() {
item?.cancel()
item = nil
super.cancel()
}
}
Having outlined the memory issues, we should note that this is all probably moot, as you probably should just make this a concurrent operation (with all that custom KVO) as you identified in the documentation. Besides, all this care we’ve put into cancelation logic only applies if the operation is alive until the asynchronous process finishes. So, we will make a concurrent operation. E.g.,
class SomeOperation: AsynchronousOperation {
private var item: DispatchWorkItem!
init(closure: #escaping () -> Void) {
super.init()
item = DispatchWorkItem { [weak self] in
closure()
self?.item = nil
self?.complete()
}
}
override func main() {
if isCancelled { return }
synchronized {
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: item)
}
}
override func cancel() {
super.cancel()
synchronized {
item?.cancel()
item = nil
}
}
}
The above uses an asynchronous operation base class that (a) performs the necessary KVO notifications; and (b) is thread-safe. Here is one random example of how that could be implemented:
/// Asynchronous Operation base class
///
/// This class performs all of the necessary KVN of `isFinished` and
/// `isExecuting` for a concurrent `NSOperation` subclass. So, to developer
/// a concurrent NSOperation subclass, you instead subclass this class which:
///
/// - must override `main()` with the tasks that initiate the asynchronous task;
///
/// - must call `complete()` function when the asynchronous task is done;
///
/// - optionally, periodically check `self.cancelled` status, performing any clean-up
/// necessary and then ensuring that `complete()` is called; or
/// override `cancel` method, calling `super.cancel()` and then cleaning-up
/// and ensuring `complete()` is called.
public class AsynchronousOperation: Operation {
private let lock = NSLock()
private var _executing: Bool = false
override private(set) public var isExecuting: Bool {
get {
synchronized { _executing }
}
set {
willChangeValue(forKey: #keyPath(isExecuting))
synchronized { _executing = newValue }
didChangeValue(forKey: #keyPath(isExecuting))
}
}
private var _finished: Bool = false
override private(set) public var isFinished: Bool {
get {
synchronized { _finished }
}
set {
willChangeValue(forKey: #keyPath(isFinished))
synchronized { _finished = newValue }
didChangeValue(forKey: #keyPath(isFinished))
}
}
override public var isAsynchronous: Bool { return true }
/// Complete the operation
///
/// This will result in the appropriate KVN of isFinished and isExecuting
public func complete() {
if isExecuting {
isExecuting = false
isFinished = true
}
}
public override func cancel() {
super.cancel()
complete()
}
override public func start() {
if isCancelled {
isFinished = true
return
}
isExecuting = true
main()
}
override public func main() {
fatalError("subclasses must override `main`")
}
public func synchronized<T>(block: () throws -> T) rethrows -> T {
try lock.synchronized { try block() }
}
}
extension NSLocking {
public func synchronized<T>(block: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try block()
}
}

Related

How do you write a Swift completion block that can only be called once?

Let's say I have a Swift class that stores a completion block, and does a few asynchronous tasks.
I want that block to be called by whichever of the tasks finishes first, but only that one - I don't want it to be called again when the second task finishes.
How can I implement this in a clean way?
As long as you don't need this to be thread safe, you can solve this problem with a fairly straightforward #propertyWrapper.
#propertyWrapper
struct ReadableOnce<T> {
var wrappedValue: T? {
mutating get {
defer { self._value = nil }
return self._value
}
set {
self._value = newValue
}
}
private var _value: T? = nil
}
Mark the completion block var with #ReadableOnce, and it will be destroyed after the first time it's value is read.
Something like this:
class MyClass {
#ReadableOnce private var completion: ((Error?) -> Void)?
init(completion: #escaping ((Error?) -> Void)) {
self.completion = completion
}
public func doSomething() {
// These could all be invoked from different places, like your separate tasks' asynchronous callbacks
self.completion?(error) // This triggers the callback, then the property wrapper sets it to nil.
self.completion?(error) // This does nothing
self.completion?(error) // This does nothing
}
}
I wrote up more of a detailed discussion of this here but the key thing to be aware of is that reading the value sets it to nil, even if you don't invoke the closure! This might be surprising to someone who isn't familiar with the clever property wrapper you've written.
There is already a standard expression of onceness. Unfortunately the standard Objective-C is unavailable in Swift (GCD dispatch_once), but the standard Swift technique works fine, namely a property with a lazy define-and-call initializer.
Exactly how you do this depends on the level at which you want onceness to be enforced. In this example it's at the level of the class instance:
class MyClass {
// private part
private let completion : (() -> ())
private lazy var once : Void = {
self.completion()
}()
private func doCompletionOnce() {
_ = self.once
}
// public-facing part
init(completion:#escaping () -> ()) {
self.completion = completion
}
func doCompletion() {
self.doCompletionOnce()
}
}
And here we'll test it:
let c = MyClass() {
print("howdy")
}
c.doCompletion() // howdy
c.doCompletion()
let cc = MyClass() {
print("howdy2")
}
cc.doCompletion() // howdy2
cc.doCompletion()
If you promote the private stuff to the level of the class (using a static once property), the completion can be performed only once in the lifetime of the entire program.

How to cancel specific Operation from OperationQueue in swift

There are 3 Operations in my OperationQueue and i'm not able to cancel specific operation from them.
I referred this example but i can't understand it
NSOperationQueue cancel specific operations
This is my code
class myOperation1 : Operation {
override func main() {
print("op1 (🐭) working....")
for i in 1...10 {
print("🐭")
}
}
}
class myOperation2 : Operation {
override func main() {
print("op2 (🐶) working....")
for i in 1...10 {
print("🐶")
}
}
}
class myOperation3 : Operation {
override func main() {
print("op3 (🍉) working....")
for i in 1...10 {
print("🍉")
}
}
}
let op1 = myOperation1()
let op2 = myOperation2()
let op3 = myOperation3()
op1.completionBlock = {
print("op1 (🐭) completed")
}
op2.completionBlock = {
print("op2 (🐶) completed")
}
op3.completionBlock = {
print("op3 (🍉) completed")
}
let opsQue = OperationQueue()
opsQue.addOperations([op1, op2, op3], waitUntilFinished: false)
DispatchQueue.global().asyncAfter(deadline: .now()) {
opsQue.cancelAllOperations()
}
Inshort i want to cancel second operation from operationQueue.
Please guide me.
Thank you
you can call op2.cancel() to cancel the operation, but you need to take additional steps to really stop your operation from running as cancel() only set the isCanceled property to true.
Please check the developer document.
https://developer.apple.com/documentation/foundation/operation/1408418-iscancelled
The default value of this property is false. Calling the cancel() method of this object sets the value of this property to true. Once canceled, an operation must move to the finished state.
Canceling an operation does not actively stop the receiver’s code from executing. An operation object is responsible for calling this method periodically and stopping itself if the method returns true.
You should always check the value of this property before doing any work towards accomplishing the operation’s task, which typically means checking it at the beginning of your custom main() method. It is possible for an operation to be cancelled before it begins executing or at any time while it is executing. Therefore, checking the value at the beginning of your main() method (and periodically throughout that method) lets you exit as quickly as possible when an operation is cancelled.
opsQue.cancelAllOperations() in your code cause removing non-started operations from queue and calls Operation.cancel() for each executing operation, but it only set isCancelled to true. You need to handle it explicitly
class myOperation2 : Operation {
override func main() {
print("op2 (🐶) working....")
for i in 1...10 {
if self.isCancelled { break } // cancelled, so interrupt
print("🐶")
}
}
}
Hope you referred to the documentation for Operation
There are several KVO-Compliant Properties for observe operation.
There is one property isCancelled - read-only
used to check this property before the execution of the operation
like this:
class myOperation2 : Operation {
override func main() {
print("op2 (🐶) working....")
if self.isCancelled {
return
}
for i in 1...10 {
print("🐶")
}
}
}
and for cancelation:
DispatchQueue.global().asyncAfter(deadline: .now()) {
opsQue.operations[1].cancel()
}

How to test method that is called with DispatchQueue.main.async?

In code I do it like this:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateBadgeValuesForTabBarItems()
}
private func updateBadgeValuesForTabBarItems() {
DispatchQueue.main.async {
self.setBadge(value: self.viewModel.numberOfUnreadMessages, for: .threads)
self.setBadge(value: self.viewModel.numberOfActiveTasks, for: .tasks)
self.setBadge(value: self.viewModel.numberOfUnreadNotifications, for: .notifications)
}
}
and in tests:
func testViewDidAppear() {
let view = TabBarView()
let model = MockTabBarViewModel()
let center = NotificationCenter()
let controller = TabBarController(view: view, viewModel: model, notificationCenter: center)
controller.viewDidLoad()
XCTAssertFalse(model.numberOfActiveTasksWasCalled)
XCTAssertFalse(model.numberOfUnreadMessagesWasCalled)
XCTAssertFalse(model.numberOfUnreadNotificationsWasCalled)
XCTAssertFalse(model.indexForTypeWasCalled)
controller.viewDidAppear(false)
XCTAssertTrue(model.numberOfActiveTasksWasCalled) //failed
XCTAssertTrue(model.numberOfUnreadMessagesWasCalled) //failed
XCTAssertTrue(model.numberOfUnreadNotificationsWasCalled) //failed
XCTAssertTrue(model.indexForTypeWasCalled) //failed
}
But all my four latest assertions failed. Why? How can I test it with success?
I think the best approach to test this is to mock the DispatchQueue. You can create a protocol that defines the functionality that you want to use:
protocol DispatchQueueType {
func async(execute work: #escaping #convention(block) () -> Void)
}
Now extend DispatchQueue to conform to your protocol, like:
extension DispatchQueue: DispatchQueueType {
func async(execute work: #escaping #convention(block) () -> Void) {
async(group: nil, qos: .unspecified, flags: [], execute: work)
}
}
Note I had to omit from the protocol the parameters you didn't use in your code, like group, qos, and flags, since protocol don't allow default values. And that's why the extension had to explicitly implement the protocol function.
Now, in your tests, create a mocked DispatchQueue that conforms to that protocol and calls the closure synchronously, like:
final class DispatchQueueMock: DispatchQueueType {
func async(execute work: #escaping #convention(block) () -> Void) {
work()
}
}
Now, all you need to do is inject the queue accordingly, perhaps in the view controller's init, like:
final class ViewController: UIViewController {
let mainDispatchQueue: DispatchQueueType
init(mainDispatchQueue: DispatchQueueType = DispatchQueue.main) {
self.mainDispatchQueue = mainDispatchQueue
super.init(nibName: nil, bundle: nil)
}
func foo() {
mainDispatchQueue.async {
*perform asynchronous work*
}
}
}
Finally, in your tests, you need to create your view controller using the mocked dispatch queue, like:
func testFooSucceeds() {
let controller = ViewController(mainDispatchQueue: DispatchQueueMock())
controller.foo()
*assert work was performed successfully*
}
Since you used the mocked queue in your test, the code will be executed synchronously, and you don't need to frustratingly wait for expectations.
You don't need to call the code in the updateBadgeValuesForTabBarItems method on the main queue.
But if you really need it, you can do something like this:
func testViewDidAppear() {
let view = TabBarView()
let model = MockTabBarViewModel()
let center = NotificationCenter()
let controller = TabBarController(view: view, viewModel: model, notificationCenter: center)
controller.viewDidLoad()
XCTAssertFalse(model.numberOfActiveTasksWasCalled)
XCTAssertFalse(model.numberOfUnreadMessagesWasCalled)
XCTAssertFalse(model.numberOfUnreadNotificationsWasCalled)
XCTAssertFalse(model.indexForTypeWasCalled)
controller.viewDidAppear(false)
let expectation = self.expectation(description: "Test")
DispatchQueue.main.async {
expectation.fullfill()
}
self.waitForExpectations(timeout: 1, handler: nil)
XCTAssertTrue(model.numberOfActiveTasksWasCalled)
XCTAssertTrue(model.numberOfUnreadMessagesWasCalled)
XCTAssertTrue(model.numberOfUnreadNotificationsWasCalled)
XCTAssertTrue(model.indexForTypeWasCalled)
}
But this is not good practice.
I had use DispatchQueue.main.asyncAfter() in my test along with expectation otherwise it was failing before text could set inside DispatchQueue.main.async {}
Methods to Test:
func setNumpadTexts(_ numpad: NumericalKeyboardVC) {
numpad.setTexts(belowNumberLabelText: Currency.symbol, enterKeyText: NSLocalizedString("Add", comment:""))
}
func setTexts(belowNumberLabelText: String? = "", enterKeyText: String) {
DispatchQueue.main.async {
self.belowNumberDisplayLbl.text = belowNumberLabelText
self.enterBtn.setTitle(enterKeyText, for: .normal)
}
}
Test:
func testSetNumpadTexts() {
sut.setNumpadTexts(numpad)
let expectation = expectation(description: "TextMatching")
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5, execute: {
//then
XCTAssertEqual(self.numpad.enterBtn.title(for: .normal), NSLocalizedString("Add", comment:""))
XCTAssertEqual(self.numpad.belowNumberDisplayLbl.text, Currency.symbol)
expectation.fulfill()
})
wait(for: [expectation], timeout: 2.0)
}
You should
Inject the dependency (DispatchQueue) into your view controller, so that you can change it in the tests
Invert the dependency using a protocol, to better conform to SOLID principles (Interface seggregation and Dependency Inversion)
Mock DispatchQueue in your tests, so that you can control your scenario
Lets apply those three items:
To invert the dependency, we will need an abstract type, that is, in Swift, a protocol. We then extend DispatchQueue to conform to that protocol
protocol Dispatching {
func async(execute workItem: DispatchWorkItem)
}
extension DispatchQueue: Dispatching {}
Next, we need to inject the dependency into our view controller. That means, pass anything that is dispatching to our view controller
final class MyViewController {
// MARK: - Dependencies
private let dispatchQueue: Dispatching // Declading that our class needs a dispatch queue
// MARK: - Initialization
init(dispatchQueue: Dispatching = DispatchQueue.main) { // Injecting the dependencies via constructor
self.dispatchQueue = dispatchQueue
super.init(nibName: nil, bundle: nil) // We must call super
}
#available(*, unavailable)
init(coder aCoder: NSCoder?) {
fatalError("We should only use our other init!")
}
// MARK: - View lifecycle
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateBadgeValuesForTabBarItems()
}
// MARK: - Private methods
private func updateBadgeValuesForTabBarItems() {
dispatchQueue.async { // Using our dependency instead of DispatchQueue directly
self.setBadge(value: self.viewModel.numberOfUnreadMessages, for: .threads)
self.setBadge(value: self.viewModel.numberOfActiveTasks, for: .tasks)
self.setBadge(value: self.viewModel.numberOfUnreadNotifications, for: .notifications)
}
}
}
Lastly, we need to create a mock for our tests. In this case, by following the testing doubles, we should create a Fake, that is, a DispatchQueue mock that doesn't really work in production, but works on our tests
final class DispatchFake: Dispatching {
func async(execute workItem: DispatchWorkItem) {
workItem.perform()
}
}
When we're testing, all we need to do is create our System Under Test(the controller, in this case), passing a fake dispatching instance
You can easy achieve this by checking if current thread is main and execute code synchronously in this case.
For example in presenter I update view in this way:
private func updateView(with viewModel: MyViewModel) {
if Thread.isMainThread {
view?.update(with: viewModel)
} else {
DispatchQueue.main.async {
self.view?.update(with: viewModel)
}
}
}
And then I can write synchronous unit tests for my presenter:
func testOnViewDidLoadFetchFailed() throws {
presenter.onViewDidLoad()
// presenter is calling interactor.fetchData when onViewDidLoad is called
XCTAssertEqual(interactor.fetchDataCallsCount, 1)
// test execute fetchData completion closure manually in the main thread
interactor.fetchDataCalls[0].completion(.failure(TestError()))
// presenter will call updateView(viewModel:) internally in synchronous way
// because we have check if Thread.isMainThread in updateView(viewModel:)
XCTAssertEqual(view.updateCallsCount, 1)
guard case .error = view.updateCalls[0] else {
XCTFail("error expected, got \(view.updateCalls[0])")
return
}
}
Here is a small proof of concept of how you could achieve it:
func testExample() {
let expectation = self.expectation(description: "numberOfActiveTasks")
var mockModel = MockModel()
mockModel.numberOfActiveTasksClosure = {() in
expectation.fulfill()
}
DispatchQueue.main.async {
_ = mockModel.numberOfActiveTasks
}
self.waitForExpectations(timeout: 2, handler: nil)
}
and here is the MockModel:
struct MockModel : Model {
var numberOfActiveTasks: Int {
get {
if let cl = numberOfActiveTasksClosure {
cl()
}
//we dont care about the actual value for this test
return 0
}
}
var numberOfActiveTasksClosure: (() -> ())?
}
To test asynchronous code you should modify your updateBadgeValuesForTabBarItems function and call it directly from your tests with a completion closure:
func updateBadgeValuesForTabBarItems(completion: (() -> Void)? = nil) {
DispatchQueue.main.async {
self.setBadge(value: self.viewModel.numberOfUnreadMessages, for: .threads)
self.setBadge(value: self.viewModel.numberOfActiveTasks, for: .tasks)
self.setBadge(value: self.viewModel.numberOfUnreadNotifications, for: .notifications)
completion?()
}
}
Now you are be able to call this function as before in your regular code e.g.: updateBadgeValuesForTabBarItems(). But for tests you can add a completion closure and use XCTestExpectation to wait:
func testBadge() {
...
let expectation = expectation(description: "Badge")
updateBadgeValuesForTabBarItems {
XCTAssertTrue(model.numberOfActiveTasksWasCalled)
XCTAssertTrue(model.numberOfUnreadMessagesWasCalled)
XCTAssertTrue(model.numberOfUnreadNotificationsWasCalled)
XCTAssertTrue(model.indexForTypeWasCalled)
expectation.fulfill()
}
wait(for: [expectation], timeout: 1)
}

Operation State Not Thread Safe Swift

I have subclassed Operation to support async operations.
The new class called AsyncOperation and added new field called state
which is an enum to help manage the operation state.
class AsyncOperation: Operation {
// DONE: State enum with keyPath property
enum State: String {
case Ready, Executing, Finished
fileprivate var keyPath: String {
return "is" + rawValue
}
}
// DONE: state property
var state = State.Ready {
willSet {
willChangeValue(forKey: newValue.keyPath)
willChangeValue(forKey: state.keyPath)
}
didSet {
didChangeValue(forKey: oldValue.keyPath)
didChangeValue(forKey: state.keyPath)
}
}
}
extension AsyncOperation {
// DONE: Operation Overrides
override var isReady: Bool {
return super.isReady && state == .Ready
}
override var isExecuting: Bool {
return state == .Executing
}
override var isFinished: Bool {
return state == .Finished
}
override var isAsynchronous: Bool {
return true
}
override func start() {
if isCancelled {
state = .Finished
return
}
main()
state = .Executing
}
override func cancel() {
state = .Finished
}
}
in general this subclass operates great and im very happy with it.
Im experiencing some odd behaviour tough...
In some cases im adding an operation to the queue like so:
//this code happens in mainViewController
//op is an operation that belong to mainViewController and could dispatched to the queue from many places, its init called once in view did load.
op = SomeAsyncOperation()
if(op.state == .Executing){
queue.addOperatiom(op)
}
and the app crashes because the operation somehow already dispatched to the queue, when i check with breakpoint the state property that i have created is Ready and the isExecuting field of the raw operation is true. what happen is my state property and the operation state fields are not synced. if i check the state field in different implementation it does get to Executing and Finished how can i be sure those will always be synced?
You should use a NSLock to guard reads and writes to the state property.
Take a look a the sample code of the session Advanced NSOperation from WWDC 2015
The important part is :
/// Private storage for the `state` property that will be KVO observed.
private var _state = State.Initialized
/// A lock to guard reads and writes to the `_state` property
private let stateLock = NSLock()
private var state: State {
get {
return stateLock.withCriticalScope {
_state
}
}
set(newState) {
/*
It's important to note that the KVO notifications are NOT called from inside
the lock. If they were, the app would deadlock, because in the middle of
calling the `didChangeValueForKey()` method, the observers try to access
properties like "isReady" or "isFinished". Since those methods also
acquire the lock, then we'd be stuck waiting on our own lock. It's the
classic definition of deadlock.
*/
willChangeValueForKey("state")
stateLock.withCriticalScope { Void -> Void in
guard _state != .Finished else {
return
}
assert(_state.canTransitionToState(newState), "Performing invalid state transition.")
_state = newState
}
didChangeValueForKey("state")
}
}

If variables, with primitive types like Int, are not atomic by default in Swift , is this the best to mimic "#synchronize" behavior? [duplicate]

In Objective-C you have a distinction between atomic and nonatomic properties:
#property (nonatomic, strong) NSObject *nonatomicObject;
#property (atomic, strong) NSObject *atomicObject;
From my understanding you can read and write properties defined as atomic from multiple threads safely, while writing and accessing nonatomic properties or ivars from multiple threads at the same time can result in undefined behavior, including bad access errors.
So if you have a variable like this in Swift:
var object: NSObject
Can I read and write to this variable in parallel safely? (Without considering the actual meaning of doing this).
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.
Swift has no language constructs around thread safety. It is assumed that you will be using the provided libraries to do your own thread safety management.
There are a large number of options you have in implementing thread safety including pthread mutexes, NSLock, and dispatch_sync as a mutex mechanism. See Mike Ash's recent post on the subject: https://mikeash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html
So the direct answer to your question of "Can I read and write to this variable in parallel safely?" is No.
It is probably to early to answer this question. Currently swift lacks access modifiers, so there is not obvious way to add code which manages concurrency around a properties getter / setter. Furthermore, the Swift Language doesn't seem to have any information about concurrency yet! (It also lacks KVO etc ...)
I think the answer to this question will become clear in future releases.
Details
Xcode 9.1, Swift 4
Xcode 10.2.1 (10E1001), Swift 5
Links
apple.developer.com Dispatch
Grand Central Dispatch (GCD) and
Dispatch Queues in Swift 3
Creating Thread-Safe Arrays in
Swift
Mutexes and closure capture in Swift
Implemented types
QueueSafeValue - a universal wrapper that provides safe multi-threaded access to values
AtomicArray
AtomicInteger
AtomicValue
Main Idea
class Example {
private lazy var semaphore = DispatchSemaphore(value: 1)
func executeThreadSafeFunc1() {
// Lock access. Only first thread can execute code below.
// Other threads will wait until semaphore.signal() will execute
semaphore.wait()
// your code
semaphore.signal() // Unlock access
}
func executeThreadSafeFunc2() {
// Lock access. Only first thread can execute code below.
// Other threads will wait until semaphore.signal() will execute
semaphore.wait()
DispatchQueue.global(qos: .background).async {
// your code
self.semaphore.signal() // Unlock access
}
}
}
Sample of atomic access
class Atomic {
let dispatchGroup = DispatchGroup()
private var variable = 0
// Usage of semaphores
func semaphoreSample() {
// value: 1 - number of threads that have simultaneous access to the variable
let atomicSemaphore = DispatchSemaphore(value: 1)
variable = 0
runInSeveralQueues { dispatchQueue in
// Only (value) queqes can run operations betwen atomicSemaphore.wait() and atomicSemaphore.signal()
// Others queues await their turn
atomicSemaphore.wait() // Lock access until atomicSemaphore.signal()
self.variable += 1
print("\(dispatchQueue), value: \(self.variable)")
atomicSemaphore.signal() // Unlock access
}
notifyWhenDone {
atomicSemaphore.wait() // Lock access until atomicSemaphore.signal()
print("variable = \(self.variable)")
atomicSemaphore.signal() // Unlock access
}
}
// Usage of sync of DispatchQueue
func dispatchQueueSync() {
let atomicQueue = DispatchQueue(label: "dispatchQueueSync")
variable = 0
runInSeveralQueues { dispatchQueue in
// Only queqe can run this closure (atomicQueue.sync {...})
// Others queues await their turn
atomicQueue.sync {
self.variable += 1
print("\(dispatchQueue), value: \(self.variable)")
}
}
notifyWhenDone {
atomicQueue.sync {
print("variable = \(self.variable)")
}
}
}
// Usage of objc_sync_enter/objc_sync_exit
func objcSync() {
variable = 0
runInSeveralQueues { dispatchQueue in
// Only one queqe can run operations betwen objc_sync_enter(self) and objc_sync_exit(self)
// Others queues await their turn
objc_sync_enter(self) // Lock access until objc_sync_exit(self).
self.variable += 1
print("\(dispatchQueue), value: \(self.variable)")
objc_sync_exit(self) // Unlock access
}
notifyWhenDone {
objc_sync_enter(self) // Lock access until objc_sync_exit(self)
print("variable = \(self.variable)")
objc_sync_exit(self) // Unlock access
}
}
}
// Helpers
extension Atomic {
fileprivate func notifyWhenDone(closure: #escaping ()->()) {
dispatchGroup.notify(queue: .global(qos: .utility)) {
closure()
print("All work done")
}
}
fileprivate func runInSeveralQueues(closure: #escaping (DispatchQueue)->()) {
async(dispatch: .main, closure: closure)
async(dispatch: .global(qos: .userInitiated), closure: closure)
async(dispatch: .global(qos: .utility), closure: closure)
async(dispatch: .global(qos: .default), closure: closure)
async(dispatch: .global(qos: .userInteractive), closure: closure)
}
private func async(dispatch: DispatchQueue, closure: #escaping (DispatchQueue)->()) {
for _ in 0 ..< 100 {
dispatchGroup.enter()
dispatch.async {
let usec = Int(arc4random()) % 100_000
usleep(useconds_t(usec))
closure(dispatch)
self.dispatchGroup.leave()
}
}
}
}
Usage
Atomic().semaphoreSample()
//Atomic().dispatchQueueSync()
//Atomic().objcSync()
Result
From Swift 5.1 you can use property wrappers to make specific logic for your properties. This is atomic wrapper implementation:
#propertyWrapper
struct atomic<T> {
private var value: T
private let lock = NSLock()
init(wrappedValue value: T) {
self.value = value
}
var wrappedValue: T {
get { getValue() }
set { setValue(newValue: newValue) }
}
func getValue() -> T {
lock.lock()
defer { lock.unlock() }
return value
}
mutating func setValue(newValue: T) {
lock.lock()
defer { lock.unlock() }
value = newValue
}
}
How to use:
class Shared {
#atomic var value: Int
...
}
Here is the atomic property wrapper that I use extensively. I made the actual locking mechanism a protocol, so I could experiement with different mechanisms. I tried semaphores, DispatchQueues, and the pthread_rwlock_t. The pthread_rwlock_t was chosen because it appears to have the lowest overhead, and a lower chance of a priority inversion.
/// Defines a basic signature that all locks will conform to. Provides the basis for atomic access to stuff.
protocol Lock {
init()
/// Lock a resource for writing. So only one thing can write, and nothing else can read or write.
func writeLock()
/// Lock a resource for reading. Other things can also lock for reading at the same time, but nothing else can write at that time.
func readLock()
/// Unlock a resource
func unlock()
}
final class PThreadRWLock: Lock {
private var rwLock = pthread_rwlock_t()
init() {
guard pthread_rwlock_init(&rwLock, nil) == 0 else {
preconditionFailure("Unable to initialize the lock")
}
}
deinit {
pthread_rwlock_destroy(&rwLock)
}
func writeLock() {
pthread_rwlock_wrlock(&rwLock)
}
func readLock() {
pthread_rwlock_rdlock(&rwLock)
}
func unlock() {
pthread_rwlock_unlock(&rwLock)
}
}
/// A property wrapper that ensures atomic access to a value. IE only one thing can write at a time.
/// Multiple things can potentially read at the same time, just not during a write.
/// By using `pthread` to do the locking, this safer then using a `DispatchQueue/barrier` as there isn't a chance
/// of priority inversion.
#propertyWrapper
public final class Atomic<Value> {
private var value: Value
private let lock: Lock = PThreadRWLock()
public init(wrappedValue value: Value) {
self.value = value
}
public var wrappedValue: Value {
get {
self.lock.readLock()
defer { self.lock.unlock() }
return self.value
}
set {
self.lock.writeLock()
self.value = newValue
self.lock.unlock()
}
}
/// Provides a closure that will be called synchronously. This closure will be passed in the current value
/// and it is free to modify it. Any modifications will be saved back to the original value.
/// No other reads/writes will be allowed between when the closure is called and it returns.
public func mutate(_ closure: (inout Value) -> Void) {
self.lock.writeLock()
closure(&value)
self.lock.unlock()
}
}

Resources