Was ist das Swift-Äquivalent zu Objective-Cs "@synchronized"?


231

Ich habe das Swift-Buch durchsucht, kann aber die Swift-Version von @synchronized nicht finden. Wie kann ich mich in Swift gegenseitig ausschließen?


1
Ich würde eine Versandbarriere verwenden. Barrieren bieten eine sehr kostengünstige Synchronisation. dispatch_barrier_async (). usw.
Frederick C. Lee

@ FrederickC.Lee, was ist, wenn Sie einen zu synchronisierenden Schreibvorgang benötigen , z. B. beim Erstellen eines Wrappers für removeFirst()?
ScottyBlades

Antworten:


183

Sie können GCD verwenden. Es ist etwas ausführlicher als @synchronized, funktioniert aber als Ersatz:

let serialQueue = DispatchQueue(label: "com.test.mySerialQueue")
serialQueue.sync {
    // code
}

12
Das ist großartig, aber es fehlt die Wiedereintrittsfähigkeit, die Sie mit @synchronized haben.
Michael Wasserfall

9
Bei diesem Ansatz müssen Sie vorsichtig sein. Ihr Block wird möglicherweise in einem anderen Thread ausgeführt. API-Dokumente sagen: "Als Optimierung ruft diese Funktion den Block des aktuellen Threads auf, wenn dies möglich ist."
Bio

20
Großartiger Artikel von Matt Gallagher darüber: cocoawithlove.com/blog/2016/06/02/threads-and-mutexes.html
wuf810

4
Nein, dies führt gelegentlich zu Deadlocks.
Tom Kraina

70
Nein, nein und nein. Netter Versuch, funktioniert aber einwandfrei. Warum? Grundlegende Lektüre (umfassender Vergleich von Alternativen, Vorsichtsmaßnahmen) und ein großartiges Utility-Framework von Matt Gallagher, hier: cocoawithlove.com/blog/2016/06/02/threads-and-mutexes.html @ wuf810 erwähnte dies zuerst (HT), aber unterschätzt, wie gut dieser Artikel ist. Alle sollten lesen. (Bitte stimmen Sie dies mindestens zu, um es zunächst sichtbar zu machen, aber nicht mehr.)
Am

181

Ich habe selbst danach gesucht und bin zu dem Schluss gekommen, dass es dafür noch kein natives Konstrukt innerhalb von Swift gibt.

Ich habe diese kleine Hilfsfunktion basierend auf dem Code erfunden, den ich von Matt Bridges und anderen gesehen habe.

func synced(_ lock: Any, closure: () -> ()) {
    objc_sync_enter(lock)
    closure()
    objc_sync_exit(lock)
}

Die Verwendung ist ziemlich einfach

synced(self) {
    println("This is a synchronized closure")
}

Es gibt ein Problem, das ich damit gefunden habe. Die Übergabe eines Arrays als Sperrargument scheint an dieser Stelle einen sehr stumpfen Compilerfehler zu verursachen. Ansonsten scheint es aber wie gewünscht zu funktionieren.

Bitcast requires both operands to be pointer or neither
  %26 = bitcast i64 %25 to %objc_object*, !dbg !378
LLVM ERROR: Broken function found, compilation aborted!

Nett! Bitte
melden

14
Dies ist ziemlich nützlich und bewahrt die Syntax des @synchronizedBlocks gut, aber beachten Sie, dass sie nicht mit einer echten eingebauten Blockanweisung wie dem @synchronizedBlock in Objective-C identisch ist , da returnund breakAnweisungen nicht mehr funktionieren, um aus der umgebenden Funktion / Schleife herauszuspringen es wäre, wenn dies eine gewöhnliche Aussage wäre.
Newacct

3
Ein Fehler ist wahrscheinlich darauf zurückzuführen, dass Arrays als Werte übergeben werden, die keine Referenzen sind
james_alvarez

9
Dies wäre wahrscheinlich ein großartiger Ort, um das neue deferSchlüsselwort zu verwenden, um sicherzustellen objc_sync_exit, dass es auch bei closureWürfen aufgerufen wird .
Devios1

3
@ t0rst Diese Antwort aufgrund des verlinkten Artikels als "fehlerhaft" zu bezeichnen, ist ungültig. Der Artikel sagt, diese Methode sei "etwas langsamer als ideal" und "auf Apple-Plattformen beschränkt". Das macht es bei weitem nicht "fehlerhaft".
RenniePet

150

Ich mag und verwende viele der Antworten hier, also würde ich wählen, welche für Sie am besten funktioniert. Die Methode, die ich bevorzuge, wenn ich so etwas wie Objective-Cs benötige, @synchronizedverwendet die deferin Swift 2 eingeführte Aussage.

{ 
    objc_sync_enter(lock)
    defer { objc_sync_exit(lock) }

    //
    // code of critical section goes here
    //

} // <-- lock released when this block is exited

Das Schöne an dieser Methode ist , dass Ihr kritischer Abschnitt den umschließende Block in jeder gewünschten Weise verlassen kann (zB return, break, continue, throw) und „die Aussagen in der Zurückstellungs Anweisung unabhängig davon ausgeführt , wie die Programmsteuerung übertragen wird.“ 1


Ich denke, dies ist wahrscheinlich die eleganteste Lösung, die hier angeboten wird. Vielen Dank für Ihr Feedback.
Scott D

3
Was ist lock? Wie wird lockinitialisiert?
Van Du Tran

6
lockist ein objektives Objekt.
ɲeuroburɳ

1
Ausgezeichnet! Ich hatte einige Lock-Helfer-Methoden geschrieben, als Swift 1 eingeführt wurde, und diese seit einiger Zeit nicht mehr wiederholt. Völlig vergessen zu verschieben; Das ist der richtige Weg!
Randy

Ich mag das, erhalte aber in Xcode 8 den Compilerfehler "Klammer-Anweisungsblock ist ein nicht verwendeter Abschluss". Ah, ich verstehe, es sind nur die Funktionsklammern - zu eine Weile, um Ihren "1" -Referenzlink zu finden - danke!
Duncan Groenewald

83

Sie können Anweisungen zwischen objc_sync_enter(obj: AnyObject?)und einfügen objc_sync_exit(obj: AnyObject?). Das Schlüsselwort @synchronized verwendet diese Methoden unter dem Deckmantel. dh

objc_sync_enter(self)
... synchronized code ...
objc_sync_exit(self)

3
Wird dies als Verwendung einer privaten API durch Apple angesehen?
Drux

2
Nein, objc_sync_enterund objc_sync_exitsind Methoden, die in Objc-sync.h definiert sind und Open Source sind: opensource.apple.com/source/objc4/objc4-371.2/runtime/…
bontoJR

Was passiert, wenn mehrere Threads versuchen, auf dieselbe Ressource zuzugreifen, der zweite wartet, es erneut versucht oder abstürzt?
TruMan1

Hinzufügen zu dem, was @bontoJR gesagt hat, objc_sync_enter(…)& objc_sync_exit(…)sind öffentliche Header, die von iOS / macOS / etc. APIs (sieht aus, als ob sie sich ….sdkim Pfad befinden usr/include/objc/objc-sync.h) . Der einfachste Weg, um herauszufinden, ob etwas eine öffentliche API ist oder nicht, besteht darin, (in Xcode) den Funktionsnamen einzugeben (z objc_sync_enter(). B. müssen für C-Funktionen keine Argumente angegeben werden) und dann mit der Befehlstaste darauf zu klicken. Wenn es Ihnen die Header-Datei für diese API zeigt, sind Sie gut (da Sie den Header nicht sehen könnten, wenn er nicht öffentlich wäre) .
Slipp D. Thompson

75

Analog zur @synchronizedDirektive von Objective-C kann es rethrowsin Swift einen beliebigen Rückgabetyp und ein nettes Verhalten geben.

// Swift 3
func synchronized<T>(_ lock: AnyObject, _ body: () throws -> T) rethrows -> T {
    objc_sync_enter(lock)
    defer { objc_sync_exit(lock) }
    return try body()
}

Durch die Verwendung der deferAnweisung können Sie einen Wert direkt zurückgeben, ohne eine temporäre Variable einzuführen.


Fügen Sie in Swift 2 das @noescapeAttribut zum Abschluss hinzu, um weitere Optimierungen zu ermöglichen:

// Swift 2
func synchronized<T>(lock: AnyObject, @noescape _ body: () throws -> T) rethrows -> T {
    objc_sync_enter(lock)
    defer { objc_sync_exit(lock) }
    return try body()
}

Basierend auf den Antworten von GNewc [1] (wo ich einen beliebigen Rückgabetyp mag) und Tod Cunningham [2] (wo ich mag defer).


Xcode sagt mir, dass @noescape jetzt Standard ist und in Swift 3 veraltet ist.
RenniePet

Der Code in dieser Antwort ist für Swift 2 und erfordert einige Anpassungen für Swift 3. Ich werde ihn aktualisieren, wenn ich Zeit dazu habe.
Werediver

1
Können Sie die Verwendung erklären? Vielleicht mit einem Beispiel .. danke im Voraus! In meinem Fall habe ich ein Set, das ich synchronisieren muss, da ich dessen Inhalt in einer DispatchQueue manipuliere.
Sancho

@sancho Ich würde es vorziehen, diesen Beitrag kurz zu halten. Sie scheinen nach allgemeinen Richtlinien für die gleichzeitige Programmierung zu fragen, das ist eine breite Frage. Versuchen Sie es als separate Frage zu stellen!
Werediver

41

SWIFT 4

In Swift 4 können Sie GCDs-Versandwarteschlangen verwenden, um Ressourcen zu sperren.

class MyObject {
    private var internalState: Int = 0
    private let internalQueue: DispatchQueue = DispatchQueue(label:"LockingQueue") // Serial by default

    var state: Int {
        get {
            return internalQueue.sync { internalState }
        }

        set (newState) {
            internalQueue.sync { internalState = newState }
        }
    }
} 

Dies scheint mit XCode8.1 nicht zu funktionieren. .serialscheint nicht verfügbar zu sein. Ist .concurrentaber vorhanden. : /
Travis Griggs

2
Die Standardeinstellung ist .serial
Duncan Groenewald

2
Beachten Sie, dass dieses Muster nicht richtig gegen die häufigsten Probleme mit mehreren Threads schützt. Wenn Sie beispielsweise myObject.state = myObject.state + 1gleichzeitig ausgeführt werden, werden nicht die Gesamtoperationen gezählt, sondern ein nicht deterministischer Wert ausgegeben. Um dieses Problem zu lösen, sollte der aufrufende Code in eine serielle Warteschlange eingeschlossen werden, damit sowohl das Lesen als auch das Schreiben atomar erfolgen. Natürlich hat Obj-c's @synchroniseddas gleiche Problem, also ist Ihre Implementierung in diesem Sinne korrekt.
Berik

1
Ja, myObject.state += 1ist eine Kombination aus einer Lese- und einer Schreiboperation. Dazwischen kann noch ein anderer Thread stehen, um einen Wert zu setzen / zu schreiben. Wie pro objc.io/blog/2018/12/18/atomic-variables , wäre es einfacher, die läuft setin einem Synchronisationsblock / Schließung statt und nicht unter der Variable selbst.
CyberMew

23

Um die Rückgabefunktionalität hinzuzufügen, können Sie Folgendes tun:

func synchronize<T>(lockObj: AnyObject!, closure: ()->T) -> T
{
  objc_sync_enter(lockObj)
  var retVal: T = closure()
  objc_sync_exit(lockObj)
  return retVal
}

Anschließend können Sie es aufrufen mit:

func importantMethod(...) -> Bool {
  return synchronize(self) {
    if(feelLikeReturningTrue) { return true }
    // do other things
    if(feelLikeReturningTrueNow) { return true }
    // more things
    return whatIFeelLike ? true : false
  }
}

23

Mit der Antwort von Bryan McLemore habe ich sie erweitert, um Objekte zu unterstützen, die ein sicheres Herrenhaus mit der Swift 2.0-Aufschubfähigkeit bieten.

func synchronized( lock:AnyObject, block:() throws -> Void ) rethrows
{
    objc_sync_enter(lock)
    defer {
        objc_sync_exit(lock)
    }

    try block()
}

Es ist besser, rethrowsdie Verwendung mit nicht werfenden Verschlüssen zu vereinfachen (keine Verwendung erforderlich try), wie in meiner Antwort gezeigt .
Werediver

10

Swift 3

Dieser Code kann erneut eingegeben werden und kann mit asynchronen Funktionsaufrufen arbeiten. In diesem Code wird nach dem Aufruf von someAsyncFunc () ein weiterer Funktionsabschluss in der seriellen Warteschlange verarbeitet, jedoch von semaphore.wait () blockiert, bis signal () aufgerufen wird. internalQueue.sync sollte nicht verwendet werden, da es den Hauptthread blockiert, wenn ich mich nicht irre.

let internalQueue = DispatchQueue(label: "serialQueue")
let semaphore = DispatchSemaphore(value: 1)

internalQueue.async {

    self.semaphore.wait()

    // Critical section

    someAsyncFunc() {

        // Do some work here

        self.semaphore.signal()
    }
}

objc_sync_enter / objc_sync_exit ist ohne Fehlerbehandlung keine gute Idee.


Welche Fehlerbehandlung? Der Compiler lässt nichts zu, was wirft. Wenn Sie dagegen objc_sync_enter / exit nicht verwenden, geben Sie einige erhebliche Leistungssteigerungen auf.
Gnasher729

8

In der Sitzung 414 "Grundlegendes zu Abstürzen und Absturzprotokollen" des WWDC 2018 wird die folgende Methode zur Verwendung von DispatchQueues mit Synchronisierung gezeigt.

In Swift 4 sollte so etwas wie das Folgende sein:

class ImageCache {
    private let queue = DispatchQueue(label: "sync queue")
    private var storage: [String: UIImage] = [:]
    public subscript(key: String) -> UIImage? {
        get {
          return queue.sync {
            return storage[key]
          }
        }
        set {
          queue.sync {
            storage[key] = newValue
          }
        }
    }
}

Auf jeden Fall können Sie Lesevorgänge auch durch gleichzeitige Warteschlangen mit Barrieren beschleunigen. Synchronisierungs- und Asynchronisierungslesevorgänge werden gleichzeitig ausgeführt, und das Schreiben eines neuen Werts wartet, bis die vorherigen Vorgänge abgeschlossen sind.

class ImageCache {
    private let queue = DispatchQueue(label: "with barriers", attributes: .concurrent)
    private var storage: [String: UIImage] = [:]

    func get(_ key: String) -> UIImage? {
        return queue.sync { [weak self] in
            guard let self = self else { return nil }
            return self.storage[key]
        }
    }

    func set(_ image: UIImage, for key: String) {
        queue.async(flags: .barrier) { [weak self] in
            guard let self = self else { return }
            self.storage[key] = image
        }
    }
}

Sie müssen wahrscheinlich keine Lesevorgänge blockieren und die Warteschlange nicht synchronisieren. Sie können die Synchronisierung nur zum seriellen Schreiben verwenden.
Basheer_CAD

6

Verwenden Sie NSLock in Swift4:

let lock = NSLock()
lock.lock()
if isRunning == true {
        print("Service IS running ==> please wait")
        return
} else {
    print("Service not running")
}
isRunning = true
lock.unlock()

Warnung Die NSLock-Klasse verwendet POSIX-Threads, um ihr Sperrverhalten zu implementieren. Wenn Sie eine Entsperrnachricht an ein NSLock-Objekt senden, müssen Sie sicherstellen, dass die Nachricht von demselben Thread gesendet wird, der die erste Sperrnachricht gesendet hat. Das Entsperren einer Sperre aus einem anderen Thread kann zu undefiniertem Verhalten führen.



6

Im modernen Swift 5 mit Rückgabefähigkeit:

/**
Makes sure no other thread reenters the closure before the one running has not returned
*/
@discardableResult
public func synchronized<T>(_ lock: AnyObject, closure:() -> T) -> T {
    objc_sync_enter(lock)
    defer { objc_sync_exit(lock) }

    return closure()
}

Verwenden Sie es wie folgt, um die Rückgabewertfunktion zu nutzen:

let returnedValue = synchronized(self) { 
     // Your code here
     return yourCode()
}

Oder anders anders:

synchronized(self) { 
     // Your code here
    yourCode()
}

2
Dies ist die richtige Antwort und nicht die akzeptierte und hoch bewertete (die davon abhängt GCD). Es scheint, dass im Wesentlichen niemand etwas benutzt oder versteht, wie man es benutzt Thread. Ich bin sehr zufrieden damit - während GCDes mit Fallstricken und Einschränkungen behaftet ist.
Javadba

4

Versuchen Sie: NSRecursiveLock

Eine Sperre, die von demselben Thread mehrmals erfasst werden kann, ohne einen Deadlock zu verursachen.

let lock = NSRecursiveLock()

func f() {
    lock.lock()
    //Your Code
    lock.unlock()
}

func f2() {
    lock.lock()
    defer {
        lock.unlock()
    }
    //Your Code
}

2

Abbildung Ich werde meine Swift 5-Implementierung veröffentlichen, die auf den vorherigen Antworten basiert. Danke Leute! Ich fand es hilfreich, eine zu haben, die auch einen Wert zurückgibt, also habe ich zwei Methoden.

Hier ist eine einfache Klasse, die zuerst erstellt werden muss:

import Foundation
class Sync {
public class func synced(_ lock: Any, closure: () -> ()) {
        objc_sync_enter(lock)
        defer { objc_sync_exit(lock) }
        closure()
    }
    public class func syncedReturn(_ lock: Any, closure: () -> (Any?)) -> Any? {
        objc_sync_enter(lock)
        defer { objc_sync_exit(lock) }
        return closure()
    }
}

Verwenden Sie es dann wie folgt, wenn Sie einen Rückgabewert benötigen:

return Sync.syncedReturn(self, closure: {
    // some code here
    return "hello world"
})

Oder:

Sync.synced(self, closure: {
    // do some work synchronously
})

Versuchen Sie public class func synced<T>(_ lock: Any, closure: () -> T), funktioniert sowohl für void als auch für jeden anderen Typ. Es gibt auch das Nachwachsen Zeug.
hnh

@hnh was meinst du mit dem Nachwachsen Zeug? Auch wenn Sie bereit wären, einen Beispielaufruf für die generische Methode mit dem Typ <T> zu teilen, der mir helfen würde, die Antwort zu aktualisieren - mir gefällt, wohin Sie damit gehen.
TheJeff

erneuert, nicht nachwächst, srz
hnh

1

Einzelheiten

xCode 8.3.1, schnell 3.1

Aufgabe

Schreibwert von verschiedenen Threads lesen (asynchron).

Code

class AsyncObject<T>:CustomStringConvertible {
    private var _value: T
    public private(set) var dispatchQueueName: String

    let dispatchQueue: DispatchQueue

    init (value: T, dispatchQueueName: String) {
        _value = value
        self.dispatchQueueName = dispatchQueueName
        dispatchQueue = DispatchQueue(label: dispatchQueueName)
    }

    func setValue(with closure: @escaping (_ currentValue: T)->(T) ) {
        dispatchQueue.sync { [weak self] in
            if let _self = self {
                _self._value = closure(_self._value)
            }
        }
    }

    func getValue(with closure: @escaping (_ currentValue: T)->() ) {
        dispatchQueue.sync { [weak self] in
            if let _self = self {
                closure(_self._value)
            }
        }
    }


    var value: T {
        get {
            return dispatchQueue.sync { _value }
        }

        set (newValue) {
            dispatchQueue.sync { _value = newValue }
        }
    }

    var description: String {
        return "\(_value)"
    }
}

Verwendung

print("Single read/write action")
// Use it when when you need to make single action
let obj = AsyncObject<Int>(value: 0, dispatchQueueName: "Dispatch0")
obj.value = 100
let x = obj.value
print(x)

print("Write action in block")
// Use it when when you need to make many action
obj.setValue{ (current) -> (Int) in
    let newValue = current*2
    print("previous: \(current), new: \(newValue)")
    return newValue
}

Vollständige Probe

Erweiterung DispatchGroup

extension DispatchGroup {

    class func loop(repeatNumber: Int, action: @escaping (_ index: Int)->(), completion: @escaping ()->()) {
        let group = DispatchGroup()
        for index in 0...repeatNumber {
            group.enter()
            DispatchQueue.global(qos: .utility).async {
                action(index)
                group.leave()
            }
        }

        group.notify(queue: DispatchQueue.global(qos: .userInitiated)) {
            completion()
        }
    }
}

Klasse ViewController

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        //sample1()
        sample2()
    }

    func sample1() {
        print("=================================================\nsample with variable")

        let obj = AsyncObject<Int>(value: 0, dispatchQueueName: "Dispatch1")

        DispatchGroup.loop(repeatNumber: 5, action: { index in
            obj.value = index
        }) {
            print("\(obj.value)")
        }
    }

    func sample2() {
        print("\n=================================================\nsample with array")
        let arr = AsyncObject<[Int]>(value: [], dispatchQueueName: "Dispatch2")
        DispatchGroup.loop(repeatNumber: 15, action: { index in
            arr.setValue{ (current) -> ([Int]) in
                var array = current
                array.append(index*index)
                print("index: \(index), value \(array[array.count-1])")
                return array
            }
        }) {
            print("\(arr.value)")
        }
    }
}

1

Mit Swifts Property Wrappern verwende ich Folgendes:

@propertyWrapper public struct NCCSerialized<Wrapped> {
    private let queue = DispatchQueue(label: "com.nuclearcyborg.NCCSerialized_\(UUID().uuidString)")

    private var _wrappedValue: Wrapped
    public var wrappedValue: Wrapped {
        get { queue.sync { _wrappedValue } }
        set { queue.sync { _wrappedValue = newValue } }
    }

    public init(wrappedValue: Wrapped) {
        self._wrappedValue = wrappedValue
    }
}

Dann können Sie einfach tun:

@NCCSerialized var foo: Int = 10

oder

@NCCSerialized var myData: [SomeStruct] = []

Greifen Sie dann wie gewohnt auf die Variable zu.


1
Ich mag diese Lösung, war aber neugierig auf die Kosten von Leuten @Decorating, da dies den Nebeneffekt hat, eine zu erstellen, DispatchQueuedie dem Benutzer verborgen bleibt. Ich fand diese SO-Referenz, um mich zu beruhigen
Adam Venturella

Der Property Wrapper selbst ist ziemlich leicht - nur eine Struktur, also eines der leichtesten Dinge, die Sie machen können. Vielen Dank für den Link auf DispatchQueue. Ich hatte im Hinterkopf einige Leistungstests für den queue.sync-Wrap im Vergleich zu anderen Lösungen (und im Vergleich zu keiner Warteschlange) durchzuführen, hatte dies aber nicht getan.
Zeichner

1

Abschließend geben Sie hier einen allgemeineren Weg an, der Rückgabewert oder void enthält, und werfen

import Foundation

extension NSObject {


    func synchronized<T>(lockObj: AnyObject!, closure: () throws -> T) rethrows ->  T
    {
        objc_sync_enter(lockObj)
        defer {
            objc_sync_exit(lockObj)
        }

        return try closure()
    }


}

0

Warum es schwierig machen und mit Schlössern Ärger machen? Verwenden Sie Versandbarrieren.

Eine Versandbarriere erstellt einen Synchronisationspunkt innerhalb einer gleichzeitigen Warteschlange.

Während der Ausführung darf kein anderer Block in der Warteschlange ausgeführt werden, selbst wenn er gleichzeitig ausgeführt wird und andere Kerne verfügbar sind.

Wenn das nach einer exklusiven (Schreib-) Sperre klingt, ist es das auch. Nicht-Barriere-Blöcke können als gemeinsam genutzte (Lese-) Sperren betrachtet werden.

Solange der gesamte Zugriff auf die Ressource über die Warteschlange erfolgt, bieten Barrieren eine sehr kostengünstige Synchronisation.


2
Ich meine, Sie gehen davon aus, dass Sie eine GCD-Warteschlange verwenden, um den Zugriff zu synchronisieren, aber das wird in der ursprünglichen Frage nicht erwähnt. Eine Barriere ist nur bei gleichzeitiger Warteschlange erforderlich. Sie können einfach eine serielle Warteschlange verwenden, um gegenseitig ausgeschlossene Blöcke in die Warteschlange zu stellen und eine Sperre zu emulieren.
Bill

Meine Frage, warum ein Schloss emulieren? Nach dem, was ich gelesen habe, werden Sperren aufgrund des Overheads gegenüber einer Barriere in einer Warteschlange nicht empfohlen.
Frederick C. Lee

0

Testen Sie anhand von „Neurobur“ einen Fall der Unterklasse

class Foo: NSObject {
    func test() {
        print("1")
        objc_sync_enter(self)
        defer {
            objc_sync_exit(self)
            print("3")
        }

        print("2")
    }
}


class Foo2: Foo {
    override func test() {
        super.test()

        print("11")
        objc_sync_enter(self)
        defer {
            print("33")
            objc_sync_exit(self)
        }

        print("22")
    }
}

let test = Foo2()
test.test()

Ausgabe:

1
2
3
11
22
33

0

dispatch_barrier_async ist der bessere Weg, ohne den aktuellen Thread zu blockieren.

dispatch_barrier_async (accessQueue, {dictionary [object.ID] = object})


-5

Eine andere Methode besteht darin, eine Oberklasse zu erstellen und diese dann zu erben. Auf diese Weise können Sie GCD direkter verwenden

class Lockable {
    let lockableQ:dispatch_queue_t

    init() {
        lockableQ = dispatch_queue_create("com.blah.blah.\(self.dynamicType)", DISPATCH_QUEUE_SERIAL)
    }

    func lock(closure: () -> ()) {
        dispatch_sync(lockableQ, closure)
    }
}


class Foo: Lockable {

    func boo() {
        lock {
            ....... do something
        }
    }

9
-1 Vererbung gibt Ihnen Subtyp-Polymorphismus als Gegenleistung für eine zunehmende Kopplung. Vermeiden Sie das spätere, wenn Sie das erstere nicht benötigen. Sei nicht faul. Bevorzugen Sie die Komposition für die Wiederverwendung von Code.
Jano
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.