Можно ли в iOS применить synchronized напрямую к переменной?

Ответ

Нет, напрямую к переменной — нельзя. Ключевое слово @synchronized из Objective-C (и аналогичные механизмы) применяются к блокам кода или методам, чтобы сделать их потокобезопасными, а не к самим переменным.

Правильный подход — синхронизировать доступ к переменной:

  1. Использование NSLock (наиболее прямой аналог @synchronized):

    let lock = NSLock()
    var sharedCounter = 0
    
    func increment() {
        lock.lock()
        defer { lock.unlock() } // Гарантирует разблокировку
        sharedCounter += 1
    }
  2. Использование serial DispatchQueue (предпочтительный способ в Swift):

    let serialQueue = DispatchQueue(label: "com.app.serialQueue")
    var sharedData: [String] = []
    
    func appendData(_ value: String) {
        serialQueue.sync { // Все операции на этой очереди выполняются последовательно
            sharedData.append(value)
        }
    }
  3. Использование os_unfair_lock (более производительный, но требующий аккуратности):

    var unfairLock = os_unfair_lock()
    var resource: Int = 0
    
    os_unfair_lock_lock(&unfairLock)
    resource = 42
    os_unfair_lock_unlock(&unfairLock)

Вывод: Синхронизируется не переменная, а критическая секция — участок кода, где происходит чтение или запись этой переменной из нескольких потоков.

Ответ 18+ 🔞

О, слушай, смотри, вот тут у людей иногда мозг сносит на раз-два. Хотят взять и навесить @synchronized прямо на переменную, как брелок на ключи. Типа, объявил — и всё, потокобезопасно, можно спать спокойно.

Да хуй там плавал, ёпта! Это же не магический амулет, который ты на шею объекту вешаешь. Это механизм, который работает с кодом, с участками, где эта самая переменная меняется или читается. Критическими секциями, блядь, называются.

Представь себе сортир на вокзале. Дверь — это твоя переменная sharedCounter. А @synchronized или NSLock — это не табличка «не влезай, убьёт» на самой двери. Это здоровенный верзила-охранник, который стоит рядом и говорит: «Очередь, сука, на хуй! Заходить по одному!». Он контролирует процесс захода-выхода (чтения-записи), а не саму дверь.

Вот смотри, как это по-свифтовски, правильно и без этих обёрток Objective-C делается:

Вариант 1 — Охранник с дубинкой (NSLock). Прямой, тупой и эффективный.

let lock = NSLock() // Наш верзила
var sharedCounter = 0 // Наша дверь в сортир

func increment() {
    lock.lock() // Верзила говорит: "Стоять, блядь! Ты следующий."
    defer { lock.unlock() } // Это гениально. Гарантия, что даже если тебя внутри трамвай переедет, он дверь откроет.
    sharedCounter += 1 // Ты делаешь свои дела.
}

Вариант 2 — Умная очередь (DispatchQueue). Цивилизованно, по-европейски.

let serialQueue = DispatchQueue(label: "com.myapp.dataQueue") // Организованная очередь, как в банке.
var sharedData: [String] = [] // Общий сейф.

func appendData(_ value: String) {
    serialQueue.sync { // Кассир говорит: "Подойдите к окошку номер один."
        // Все, кто через эту очередь идёт, идут строго по одному.
        sharedData.append(value) // Кладёшь деньги в сейф.
    }
}

Вариант 3 — Спецназовец (os_unfair_lock). Быстро, жёстко, но если облажаешься — сам себе злобный Буратино.

var unfairLock = os_unfair_lock() // Боевой нож, а не дубинка.
var resource: Int = 0

os_unfair_lock_lock(&unfairLock) // Быстро взял за горло.
resource = 42 // Сделал дело.
os_unfair_lock_unlock(&unfairLock) // Отпустил. Главное — не забыть, а то задушишь поток нахуй.

Так что запомни, как «Отче наш»: синхронизируется не переменная, а действия с ней. Участок кода, где к ней прикасаются из разных потоков. Всё остальное — это пиздёж и провокация, ведущая к гонкам, крешам и ночным бдениям с отладчиком. Чистый изумруд, блядь!