Является ли Core Data по умолчанию потокобезопасной?

Ответ

Нет. Core Data не является потокобезопасной по умолчанию. Объекты NSManagedObjectContext и NSManagedObject должны использоваться только в том потоке (очереди), в котором они были созданы.

Основные правила потокобезопасности:

  1. Каждый контекст (NSManagedObjectContext) привязан к конкретной очереди.
  2. Управляемые объекты (NSManagedObject) нельзя напрямую передавать между контекстами из разных потоков.

Безопасная работа с контекстами: Используйте методы perform или performAndWait для выполнения операций в правильной очереди.

// Создание приватного контекста для фоновой работы
let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateContext.parent = mainContext

privateContext.perform {
    // Все операции с контекстом внутри этого блока
    let newItem = Item(context: privateContext)
    newItem.name = "Sample"

    do {
        try privateContext.save() // Сохраняем в приватный контекст
        // Для сохранения в persistent store координатору может потребоваться
        // сохранение родительского (main) контекста.
    } catch {
        print("Save error: (error)")
    }

    // Обновление UI должно происходить на главном потоке
    DispatchQueue.main.async {
        // Обновить интерфейс
    }
}

Рекомендуемые практики для многопоточности:

  • Используйте соответствующий тип контекста: .mainQueueConcurrencyType для UI, .privateQueueConcurrencyType для фоновых задач.
  • Для передачи изменений между контекстами используйте родительско-дочерние иерархии или слияние через уведомления (NSManagedObjectContextDidSave).
  • Для передачи идентификаторов объектов между очередями используйте NSManagedObjectID.

Ответ 18+ 🔞

А, ну это, классика, блядь! Core Data и потоки — история старая, как мир, и хуёвая, как вчерашний борщ. Слушай сюда, распиздяй.

Нет, сука, неееет! Core Data — она не потокобезопасная, вот нихуя! Это не та штука, которую можно как попало по потокам швырять. Объекты эти, NSManagedObjectContext и NSManagedObject, они как привязанные к своей очереди, блядь. Создали в одном потоке — там и юзайте, пока не надоест. Передавать их в другой поток — это прямой билет в краш, в пизду, в лучших традициях.

Главные правила, которые надо вбить себе в башку:

  1. Каждый контекст (NSManagedObjectContext) — это как своя вотчина, своя очередь. Не лезь со своим уставом в чужой монастырь.
  2. Управляемые объекты (NSManagedObject) — это вообще штука одноразовая в плане потоков. Нельзя просто так взять и перекинуть из одного контекста в другой, если они из разных потоков. Хуй с горы получишь, а не результат.

Как не обосраться с контекстами: Юзай методы perform или performAndWait. Это как волшебные слова, которые говорят: «Эй, контекст, делай всё в своей родной очереди, а не где попало».

// Создаём приватный контекст для всякой фоновой хуйни
let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateContext.parent = mainContext // Главный контекст — папаша

privateContext.perform { // Всё, вошли в его вотчину
    // Теперь тут можно делать что угодно с этим контекстом
    let newItem = Item(context: privateContext)
    newItem.name = "Sample"

    do {
        try privateContext.save() // Сохранили в приватный контекст
        // Чтобы добралось до постоянного хранилища, может, ещё и главный контекст сохранить придётся.
    } catch {
        print("Save error: (error)") // Ну вот, опять обосрались
    }

    // А вот обновлять интерфейс — это уже на главный поток, ёпта!
    DispatchQueue.main.async {
        // Обновляем UI
    }
}

Чтобы жизнь мёдом не казалась, запомни ещё:

  • Тип контекста выбирай с умом: .mainQueueConcurrencyType — для всего, что связано с интерфейсом, а .privateQueueConcurrencyType — для всех этих фоновых вычислительных пизделок.
  • Чтобы изменения между контекстами передавать, либо делай их родителями-детьми, либо лови уведомления (NSManagedObjectContextDidSave) и мерьджь, как последний урод.
  • Если очень надо передать ссылку на объект в другую очередь — тащи не сам объект, а его NSManagedObjectID. Это как паспорт объекта, по нему в любом контексте можно найти его копию. А сам объект таскать — это, блядь, верный способ всё сломать.

Вот и вся магия, ёпта. Не усложняй, делай по правилам, и будет тебе счастье. А нет — ну, сам знаешь куда.