Le variabili Swift sono atomiche?

In Objective-C hai una distinzione tra proprietà atomiche e non anatomiche:

@property (nonatomic, strong) NSObject *nonatomicObject; @property (atomic, strong) NSObject *atomicObject; 

Dalla mia comprensione è ansible leggere e scrivere proprietà definite come atomiche da più thread in modo sicuro, mentre la scrittura e l’accesso a proprietà nonatomiche o ivar da più thread contemporaneamente può provocare un comportamento indefinito, inclusi errori di accesso errati.

Quindi se hai una variabile come questa in Swift:

 var object: NSObject 

Posso leggere e scrivere su questa variabile in parallelo in modo sicuro? (Senza considerare il vero significato di farlo).

È molto presto per presupporre che non è disponibile una documentazione di basso livello, ma è ansible studiare dall’assemblaggio. Hopper Disassembler è un ottimo strumento.

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

Utilizza objc_storeStrong e objc_setProperty_atomic per nonatomici e atomici, dove

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

usa swift_retain da libswift_stdlib_core e, apparentemente, non ha integrato la sicurezza del thread.

Possiamo ipotizzare che ulteriori parole chiave (simili a @lazy ) possano essere introdotte in seguito.

Aggiornamento del 20/07/2015 : secondo questo blogpost su singletons, l’ ambiente rapido può rendere sicuri certi casi per voi, ovvero:

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

Aggiornamento 05/25/16 : Tieni d’occhio la proposta di una rapida evoluzione https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md – sembra che sia sarà ansible avere il comportamento @atomic implementato da te stesso.

Swift non ha costrutti linguistici attorno alla sicurezza dei thread. Si presume che utilizzerai le librerie fornite per eseguire la gestione della sicurezza del thread. Ci sono un gran numero di opzioni che si hanno nell’implementazione della sicurezza dei thread inclusi i mutex pthread, NSLock e dispatch_sync come meccanismo mutex. Guarda il recente post di Mike Ash sull’argomento: https://mikeash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html Quindi la risposta diretta alla tua domanda di “Can Ho letto e scritto questa variabile in parallelo in modo sicuro? ” è no.

Probabilmente è presto per rispondere a questa domanda. Attualmente non sono necessari modificatori di accesso, quindi non esiste un modo ovvio per aggiungere codice che gestisca la concorrenza attorno a un getter / setter di proprietà. Inoltre, la Swift Language non sembra avere ancora alcuna informazione sulla concorrenza! (Manca anche KVO ecc …)

Penso che la risposta a questa domanda diventerà chiara nelle versioni future.

Dettagli

Xcode 9.1, Swift 4

link

  • apple.developer.com Dispatch
  • Grand Central Dispatch (GCD) e Code di spedizione in Swift 3
  • Creazione di array thread-safe in Swift
  • Mutex e chiusura catturati in Swift

Campione di accesso atomico

 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() } } } } 

uso

 Atomic().semaphoreSample() //Atomic().dispatchQueueSync() //Atomic().objcSync() 

Risultato

inserisci la descrizione dell'immagine qui