Операции Core Data в нескольких потоках вызывают вставку, но не выборку

Я использую CoreData для вставки данных и извлечения данных, поскольку у меня много данных, поэтому я использую основные данные в нескольких потоках, чтобы быть потокобезопасным.

Проблема в том, что я могу вставить данные в CoreData, но при извлечении из CoreData результаты нулевые, это происходит, когда я закрываю свое приложение и извлекаю данные из базы данных. Это как-то связано с NSMangedObjectContext, но я не могу понять.

Вот мой фрагмент кода:

class CoreDataManager {

    static let sharedManager = CoreDataManager()
    private init() {}

    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "My_Contacts")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()

    func saveContext() {
        let context = CoreDataManager.sharedManager.persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }

    func insertContact(id:Int, firstName : String,lastName : String,emaild : String,isFavorite : Bool,phoneNum : String,profilePic : String,sync : Bool) -> Contact? {
        let managedContext = CoreDataManager.sharedManager.persistentContainer.viewContext
        let privateManagedObjectContext: NSManagedObjectContext = {
            //NSPrivateQueueConcurrencyType
            let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
            moc.parent = managedContext
            return moc
        }()


        let entity = NSEntityDescription.entity(forEntityName: "Contact",
                                                in: privateManagedObjectContext)!
        let contact = NSManagedObject(entity: entity,
                                     insertInto: privateManagedObjectContext)
        contact.setValue(firstName, forKey: "first_name")
        contact.setValue(lastName, forKey: "last_name")
        contact.setValue(emaild, forKey: "email")
        contact.setValue(isFavorite, forKey: "favorite")
        contact.setValue(phoneNum, forKey: "phone_number")
        contact.setValue(profilePic, forKey: "profile_pic")
        contact.setValue(sync, forKey: "syncStatus")
        contact.setValue(id, forKey: "contactId")

        do {
            try privateManagedObjectContext.save()
            return contact as? Contact
        } catch let error as NSError {
            print("Could not save. \(error), \(error.userInfo)")
            return nil
        }
    }


    func fetchAllContacts() -> [Contact]?{

        let managedContext = CoreDataManager.sharedManager.persistentContainer.viewContext
        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Contact")

        do {
            let people = try managedContext.fetch(fetchRequest)
            return people as? [Contact]
        } catch let error as NSError {
            print("Could not fetch. \(error), \(error.userInfo)")
            return nil
        }
    }
}

person Samarth Kejriwal    schedule 10.06.2019    source источник
comment
Подтвердите, что ваша операция сохранения прошла успешно.   -  person Aravind Bhuvanendran    schedule 10.06.2019


Ответы (2)


поэтому я использую основные данные в нескольких потоках, чтобы быть потокобезопасным

Что вы имеете в виду под этим? Использование нескольких потоков не делает ничего потокобезопасным. Потокобезопасность связана с вашей способностью без проблем запускать свой код в нескольких потоках и обычно требует принятия ряда мер предосторожности, чтобы потоки не мешали друг другу.

Проблема в том, что я могу вставить данные в CoreData, но при извлечении из CoreData результаты нулевые, это происходит, когда я убиваю свое приложение и извлекаю данные из базы данных. Это как-то связано с NSMangedObjectContext, но я не могу этого понять.

Вы должны понимать, что такое контекст управляемого объекта. Думайте об этом как о временном рабочем пространстве: вы можете выполнить запрос на выборку, чтобы перенести объекты из постоянного хранилища в контекст управляемого объекта, и вы можете добавить новые объекты в контекст, и вы можете манипулировать объектами в контексте. Изменения, которые вы делаете в контексте, ничего не значат вне контекста, пока вы не сохраните контекст обратно в постоянное хранилище.

Несколько причин, по которым вы можете не видеть добавляемые объекты:

  • Вы добавляете объекты и пытаетесь прочитать их в разных контекстах.

  • Вы никогда не сохраняете контекст после добавления объектов.

  • Вы сохраняете контекст, в котором вы добавили объект, но родительский контекст (контексты управляемых объектов являются иерархическими) никогда не сохраняется.

  • Вы пытаетесь сохранить контекст после добавления объектов, но сохранение не удается.

  • Вы используете один и тот же контекст в нескольких потоках, не заботясь о сериализации операций над контекстом (то есть ваш код не является потокобезопасным).

Что вам действительно нужно сделать, чтобы понять это, так это вернуться в состояние, в котором вы можете надежно хранить и извлекать объекты. Попробуйте использовать только один поток и убедитесь, что ваши операции работают. Если они этого не делают, исправьте это в первую очередь. Затем получите четкое представление о том, как работают контексты управляемых объектов и как их использовать. Наконец, прочитайте параллелизм и основные данные.

person Caleb    schedule 10.06.2019
comment
Спасибо за четкое понимание, я не сохранял родительский контекст, поэтому данные не сохранялись в постоянных хранилищах. - person Samarth Kejriwal; 10.06.2019

Поскольку вы используете несколько MOC (контекст управляемых объектов), вам необходимо сохранить оба контекста.

Вы установили родителем privateManagedObjectContext значение managedContext, но не сохраняете managedContext

После звонка privateManagedObjectContext.save() нужно позвонить и managedContext.save()

person Shubham Bakshi    schedule 10.06.2019
comment
yaa, это была проблема, которая происходила. Спасибо :) - person Samarth Kejriwal; 10.06.2019
comment
@SamarthKejriwal Спасибо! Если вы считаете, что ответ помог вам, отметьте его как принятое решение. Спасибо - person Shubham Bakshi; 10.06.2019
comment
Но это блокирует мой основной поток до тех пор, пока моя операция вставки не завершится. - person Samarth Kejriwal; 10.06.2019
comment
Это потому, что ваш managedContext – это ваш viewContext, принадлежащий вашему основному потоку. - person Shubham Bakshi; 10.06.2019
comment
Да, я понял, но как сохранить parentContext, не блокируя пользовательский интерфейс - person Samarth Kejriwal; 10.06.2019