Apa yang dimaksud dengan Swift yang setara dengan Objective-C “@synchronized”?


231

Saya telah mencari di buku Swift, tetapi tidak dapat menemukan versi Swift dari @sinkronisasi. Bagaimana saya melakukan pengecualian bersama dalam Swift?


1
Saya akan menggunakan penghalang pengiriman. Hambatan menyediakan sinkronisasi yang sangat murah. dispatch_barrier_async (). dll.
Frederick C. Lee

@ FrederickC.Lee, bagaimana jika Anda perlu menulis untuk disinkronkan, seperti ketika membuat pembungkus untuk removeFirst()?
ScottyBlades

Jawaban:


183

Anda bisa menggunakan GCD. Ini sedikit lebih verbose daripada @synchronized, tetapi berfungsi sebagai pengganti:

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

12
Ini bagus, tetapi tidak ada kemampuan masuk kembali yang Anda miliki dengan @sinkronisasi.
Air Terjun Michael

9
Dengan pendekatan ini Anda harus berhati-hati. Blok Anda mungkin dieksekusi di beberapa utas lainnya. Dokumen API mengatakan: "Sebagai optimasi, fungsi ini memanggil blok pada utas saat ini jika memungkinkan."
bio

20
Artikel hebat dari Matt Gallagher tentang ini: cocoawithlove.com/blog/2016/06/02/threads-and-mutexes.html
wuf810

4
Tidak, ini menyebabkan kebuntuan sesekali.
Tom Kraina

71
Tidak, tidak dan tidak. Usaha yang bagus, tetapi bekerja dengan sangat baik. Mengapa? Bacaan penting (perbandingan komprehensif dari alternatif, peringatan) dan kerangka kerja utilitas yang bagus dari Matt Gallagher, di sini: cocoawithlove.com/blog/2016/06/02/threads-and-mutexes.html @ wuf810 menyebutkan ini lebih dulu (HT), tetapi mengecilkan seberapa bagus artikel ini. Semua harus dibaca. (Harap pilih ini seminimal mungkin agar awalnya terlihat, tetapi tidak lebih.)
t0rst

181

Saya sendiri sedang mencari ini dan sampai pada kesimpulan belum ada konstruksi asli dalam swift untuk ini.

Saya membuat fungsi pembantu kecil ini berdasarkan pada beberapa kode yang saya lihat dari Matt Bridges dan yang lainnya.

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

Penggunaannya cukup mudah

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

Ada satu masalah yang saya temukan dengan ini. Melewati dalam array sebagai argumen kunci tampaknya menyebabkan kesalahan kompiler yang sangat tumpul pada saat ini. Kalau tidak, tampaknya berfungsi seperti yang diinginkan.

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!

Bagus! Silakan ajukan bug untuk itu jika masih ada masalah di 1.0
MattD

14
Ini cukup berguna dan mempertahankan sintaks @synchronizedblok dengan baik, tetapi perhatikan bahwa itu tidak identik dengan pernyataan blok builtin nyata seperti @synchronizedblok di Objective-C, karena returndan breakpernyataan tidak lagi berfungsi untuk melompat keluar dari fungsi / loop di sekitarnya seperti itu akan terjadi jika ini adalah pernyataan biasa.
Newacct

3
Kesalahan kemungkinan karena array dilewatkan sebagai nilai bukan referensi
james_alvarez

9
Ini mungkin akan menjadi tempat yang bagus untuk menggunakan deferkata kunci baru untuk memastikan objc_sync_exitdipanggil bahkan jika closuremelempar.
devios1

3
@ t0rst Memanggil jawaban ini "cacat" berdasarkan artikel yang ditautkan ke tidak valid. Artikel itu mengatakan metode ini "sedikit lebih lambat dari yang ideal" dan "terbatas pada platform Apple". Itu tidak membuatnya "cacat" oleh tembakan panjang.
RenniePet

151

Saya suka dan menggunakan banyak jawaban di sini, jadi saya akan memilih mana yang paling cocok untuk Anda. Yang mengatakan, metode yang saya sukai ketika saya membutuhkan sesuatu seperti objektif-c @synchronizedmenggunakan deferpernyataan yang diperkenalkan di Swift 2.

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

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

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

Bagus hal tentang metode ini, adalah bahwa bagian kritis Anda dapat keluar blok yang mengandung dalam setiap busana yang diinginkan (misalnya, return, break, continue, throw), dan "laporan dalam pernyataan defer dijalankan tidak peduli bagaimana kontrol program ditransfer." 1


Saya pikir ini mungkin solusi paling elegan yang disediakan di sini. Terima kasih atas tanggapan Anda.
Scott D

3
Apa lock? Bagaimana lockdiinisialisasi?
Van Du Tran

6
lockadalah objek objektif-c.
ɲeuroburɳ

1
Luar biasa! Saya telah menulis beberapa metode pembantu kunci ketika Swift 1 diperkenalkan dan belum mengunjungi kembali ini dalam beberapa saat. Benar-benar lupa tentang penundaan; ini cara untuk pergi!
Randy

Saya suka ini, tetapi mendapatkan kesalahan kompiler "Blok pernyataan pernyataan adalah penutupan yang tidak digunakan" di Xcode 8. Ah saya mengerti mereka hanyalah kurung fungsi - terlalu lama untuk menemukan tautan referensi "1" - terima kasih!
Duncan Groenewald

83

Anda dapat menjepit pernyataan antara objc_sync_enter(obj: AnyObject?)dan objc_sync_exit(obj: AnyObject?). Kata kunci @sinkronisasi menggunakan metode-metode di bawah penutup. yaitu

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

3
Apakah ini akan dianggap sebagai penggunaan API pribadi oleh Apple?
Drux

2
Tidak, objc_sync_enterdan objc_sync_exitapakah metode didefinisikan dalam Objc-sync.h dan merupakan sumber terbuka: opensource.apple.com/source/objc4/objc4-371.2/runtime/…
bontoJR

Apa yang terjadi jika beberapa utas mencoba mengakses sumber yang sama, apakah yang kedua menunggu, mencoba lagi, atau mogok?
TruMan1

Menambah apa yang dikatakan @bontoJR, objc_sync_enter(…) & objc_sync_exit(…)adalah tajuk publik yang disediakan oleh iOS / macOS / etc. API (sepertinya ada di dalam ….sdkdi jalur usr/include/objc/objc-sync.h) . Cara termudah untuk mengetahui apakah sesuatu adalah API publik atau tidak adalah dengan (dalam Xcode) ketik nama fungsi (misalnya objc_sync_enter(); argumen tidak perlu ditentukan untuk fungsi C) , lalu coba klik perintah. Jika itu menunjukkan kepada Anda file header untuk API itu, maka Anda baik (karena Anda tidak akan dapat melihat header jika itu tidak publik) .
Slipp D. Thompson

75

Analog @synchronizedarahan dari Objective-C dapat memiliki jenis pengembalian sewenang-wenang dan rethrowsperilaku yang baik di Swift.

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

Penggunaan deferpernyataan memungkinkan untuk secara langsung mengembalikan nilai tanpa memperkenalkan variabel sementara.


Di Swift 2 tambahkan @noescapeatribut ke penutupan untuk memungkinkan lebih banyak optimisasi:

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

Berdasarkan jawaban dari GNewc [1] (di mana saya suka tipe pengembalian sewenang-wenang) dan Tod Cunningham [2] (di mana saya suka defer).


Xcode memberi tahu saya bahwa @noescape sekarang default dan sudah usang dalam Swift 3.
RenniePet

Benar, kode dalam jawaban ini adalah untuk Swift 2 dan memerlukan beberapa adaptasi untuk Swift 3. Saya akan memperbaruinya ketika saya punya waktu.
werediver

1
Bisakah Anda menjelaskan penggunaannya? Mungkin dengan contoh .. terima kasih sebelumnya! Dalam kasus saya, saya memiliki Perangkat yang perlu saya sinkronkan, karena saya memanipulasi kontennya dalam DispatchQueue.
sancho

@ sancho Saya lebih suka untuk menjaga posting ini ringkas. Anda tampaknya bertanya tentang pedoman pemrograman bersamaan umum, itu pertanyaan yang luas. Cobalah untuk menanyakannya sebagai pertanyaan terpisah!
werediver

41

SWIFT 4

Di Swift 4 Anda dapat menggunakan antrian pengiriman GCD untuk mengunci sumber daya.

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

Ini sepertinya tidak berfungsi dengan XCode8.1. .serialtampaknya tidak tersedia. Tetapi .concurrenttersedia. : /
Travis Griggs

2
standarnya adalah .serial
Duncan Groenewald

2
Perhatikan bahwa pola ini tidak melindungi dengan benar terhadap masalah multi utas yang paling umum. Misalnya, jika Anda menjalankan myObject.state = myObject.state + 1secara bersamaan, itu tidak akan menghitung total operasi tetapi menghasilkan nilai nondeterministic. Untuk mengatasi masalah itu, kode panggilan harus dibungkus dalam antrian serial sehingga membaca dan menulis terjadi secara atomis. Tentu saja Obj-c @synchronisedmemiliki masalah yang sama, jadi dalam arti implementasi Anda sudah benar.
Berik

1
Ya, myObject.state += 1merupakan kombinasi dari operasi baca dan kemudian penulisan. Beberapa utas lainnya masih dapat datang di antara untuk menetapkan / menulis nilai. Sesuai objc.io/blog/2018/12/18/atomic-variables , akan lebih mudah untuk menjalankan setblok / penutupan sinkronisasi sebagai gantinya dan tidak di bawah variabel itu sendiri.
CyberMew

24

Menggunakan jawaban Bryan McLemore, saya memperluasnya untuk mendukung benda-benda yang melemparkan manor yang aman dengan kemampuan menunda Swift 2.0.

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

    try block()
}

Akan lebih baik digunakan rethrowsuntuk menyederhanakan penggunaan dengan penutupan non-lempar (tidak perlu digunakan try), seperti yang ditunjukkan dalam jawaban saya .
werediver

23

Untuk menambahkan fungsionalitas pengembalian, Anda dapat melakukan ini:

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

Selanjutnya, Anda dapat memanggilnya menggunakan:

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

10

Cepat 3

Kode ini memiliki kemampuan masuk kembali dan dapat bekerja dengan panggilan fungsi Asynchronous. Dalam kode ini, setelah someAsyncFunc () dipanggil, penutupan fungsi lain pada antrian serial akan memproses tetapi diblokir oleh semaphore.wait () hingga signal () dipanggil. internalQueue.sync tidak boleh digunakan karena akan memblokir utas utama jika saya tidak salah.

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 bukan ide yang baik tanpa penanganan kesalahan.


Penanganan kesalahan apa? Kompiler tidak akan mengizinkan apa pun yang melempar. Di sisi lain, dengan tidak menggunakan objc_sync_enter / exit, Anda menyerah pada beberapa keuntungan kinerja yang substansial.
gnasher729

8

Dalam sesi "Memahami Kecelakaan dan Kecelakaan" 414 dari WWDC 2018, mereka menunjukkan cara berikut menggunakan DispatchQueues dengan sinkronisasi.

Di swift 4 harus seperti ini:

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

Pokoknya Anda juga dapat membuat pembacaan lebih cepat menggunakan antrian bersamaan dengan hambatan. Sinkronisasi dan pembacaan async dilakukan bersamaan dan menulis nilai baru menunggu operasi sebelumnya selesai.

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

Anda mungkin tidak perlu memblokir pembacaan dan memperlambat antrian menggunakan sinkronisasi. Anda bisa menggunakan sinkronisasi untuk penulisan serial.
Basheer_CAD

6

Gunakan NSLock di 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()

Peringatan Kelas NSLock menggunakan utas POSIX untuk menerapkan perilaku pengunciannya. Saat mengirim pesan pembuka kunci ke objek NSLock, Anda harus yakin bahwa pesan dikirim dari utas yang sama dengan yang mengirim pesan kunci awal. Membuka kunci kunci dari utas yang berbeda dapat mengakibatkan perilaku yang tidak terdefinisi.



6

Di Swift 5 modern, dengan kemampuan pengembalian:

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

Gunakan seperti ini, untuk memanfaatkan kapabilitas nilai pengembalian:

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

Atau seperti itu sebaliknya:

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

2
Ini adalah jawaban yang benar dan bukan jawaban yang diterima dan sangat dijunjung tinggi (yang tergantung pada GCD). Tampaknya pada dasarnya tidak ada yang menggunakan atau mengerti cara menggunakannya Thread. Saya senang dengan itu - sedangkan GCDpenuh dengan gotcha dan keterbatasan.
javadba

4

Coba: NSRecursiveLock

Kunci yang mungkin diperoleh beberapa kali oleh utas yang sama tanpa menyebabkan kebuntuan.

let lock = NSRecursiveLock()

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

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

2

Gambar Saya akan memposting implementasi Swift 5 saya, dibangun dari jawaban sebelumnya. Terima kasih kawan! Saya merasa terbantu memiliki satu yang mengembalikan nilai juga, jadi saya punya dua metode.

Berikut ini adalah kelas sederhana yang harus dibuat terlebih dahulu:

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

Kemudian gunakan seperti itu jika membutuhkan nilai kembali:

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

Atau:

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

Coba public class func synced<T>(_ lock: Any, closure: () -> T), bekerja untuk keduanya, batal dan jenis lainnya. Ada juga hal-hal yang tumbuh kembali.
hnh

@ hnh apa yang Anda maksud dengan hal-hal yang tumbuh kembali? Juga jika Anda bersedia membagikan contoh panggilan ke metode umum dengan tipe <T> yang akan membantu saya memperbarui jawabannya - Saya suka ke mana Anda akan pergi dengan itu.
TheJeff

rethrows, bukan regrows, srz
hnh

1

Detail

xCode 8.3.1, cepat 3.1

Tugas

Baca nilai tulis dari utas berbeda (async).

Kode

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

Pemakaian

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
}

Sampel Lengkap

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

kelas 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

Dengan pembungkus properti Swift, inilah yang saya gunakan sekarang:

@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
    }
}

Maka Anda bisa melakukan:

@NCCSerialized var foo: Int = 10

atau

@NCCSerialized var myData: [SomeStruct] = []

Kemudian akses variabel seperti biasa.


1
Saya suka solusi ini, tetapi ingin tahu tentang biaya orang @Decorating karena melakukan itu memiliki efek samping membuat DispatchQueueyang tersembunyi dari pengguna. Saya menemukan referensi SO ini untuk menenangkan pikiran saya: stackoverflow.com/a/35022486/1060314
Adam Venturella

Wrapper properti itu sendiri cukup ringan - hanya struct, jadi, salah satu hal paling ringan yang dapat Anda buat. Terima kasih atas tautannya di DispatchQueue. Saya sudah di belakang pikiran saya untuk melakukan beberapa pengujian kinerja pada bungkus antrian.sync versus solusi lain (dan versus tidak ada antrian), tetapi belum melakukannya.
drewster

1

Kesimpulannya, Di sini memberikan cara yang lebih umum yang mencakup nilai balik atau batal, dan melempar

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

Mengapa membuatnya sulit dan repot dengan kunci? Gunakan Dispatch Barriers.

Penghalang pengiriman membuat titik sinkronisasi dalam antrian bersamaan.

Ketika sedang berjalan, tidak ada blok lain di antrian yang diizinkan untuk berjalan, bahkan jika itu bersamaan dan inti lainnya tersedia.

Jika itu terdengar seperti kunci (tulis) eksklusif, ya. Blok non-penghalang dapat dianggap sebagai kunci bersama (baca).

Selama semua akses ke sumber daya dilakukan melalui antrian, hambatan menyediakan sinkronisasi yang sangat murah.


2
Maksud saya, Anda mengasumsikan penggunaan antrian GCD untuk menyinkronkan akses, tetapi itu tidak disebutkan dalam pertanyaan awal. Dan penghalang hanya diperlukan dengan antrian bersamaan - Anda cukup menggunakan antrian serial untuk mengantri blok yang dikecualikan bersama untuk meniru kunci.
Bill,

Pertanyaan saya, mengapa meniru kunci? Dari apa yang saya baca, kunci tidak disarankan karena overhead vs penghalang dalam antrian.
Frederick C. Lee

0

Berdasarkan ɲeuroburɳ , uji kasus kelas bawah

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

Keluaran:

1
2
3
11
22
33

0

dispatch_barrier_async adalah cara yang lebih baik, sementara tidak memblokir utas saat ini.

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


-5

Metode lain adalah membuat superclass dan kemudian mewarisinya. Dengan cara ini Anda dapat menggunakan GCD secara langsung

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

10
-1 Warisan memberi Anda polimorfisme subtipe dengan imbalan peningkatan kopling. Hindari nanti jika Anda tidak membutuhkan yang pertama. Jangan malas. Lebih suka komposisi untuk penggunaan kembali kode.
Jano
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.