如何同时从3个不同的线程管理对DB的更新,删除,获取操作?

问题描述 投票:-1回答:2

如何在iOS中同时从3个不同的线程管理对DB的更新,删除,获取操作?

ios swift database
2个回答
0
投票

您可以使用并发线程在核心数据中执行多项操作,例如添加,更新,删除。

import CoreData




struct CoreDataStack {
    // MARK: Properties
    private let model: NSManagedObjectModel
    internal let coordinator: NSPersistentStoreCoordinator
    private let modelURL: URL
    internal let dbURL: URL
    let context: NSManagedObjectContext

    // MARK: Initializers

    init?(modelName: String) {

        // MARK: This one should be from the framework bundle
        // Assumes the model is in the main bundle

        guard let modelURL = Bundle.main.url(forResource: modelName, withExtension: "momd") else {
            fatalError("Unable to find \(modelName)in the framework bundle")
            return nil
        }
        self.modelURL = modelURL

        // Try to create the model from the URL
        guard let model = NSManagedObjectModel(contentsOf: modelURL) else {
            fatalError("unable to create a model from \(modelURL)")
            return nil
        }
        self.model = model

        // Create the store coordinator
        coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)

        // create a context and add connect it to the coordinator
        context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
        context.persistentStoreCoordinator = coordinator

        // When unique constrains is violated then new value will replace the old value
        context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

        // Add a SQLite store located in the documents folder
        let fm = FileManager.default

        guard let docUrl = fm.urls(for: .documentDirectory, in: .userDomainMask).first else {

            return nil
        }

        //SQLite DB file name
        self.dbURL = docUrl.appendingPathComponent("Model.sqlite")

        do {
            try addStoreCoordinator(NSSQLiteStoreType, configuration: nil, storeURL: dbURL, options: nil)
        } catch {
            //Error
        }
    }

    // MARK: Utils

    func addStoreCoordinator(_ storeType: String, configuration: String?, storeURL: URL, options : [NSObject:AnyObject]?) throws {
        var storeOptions = [String: AnyObject]()
        //Option to enable file level security to protect the DB file
        storeOptions[NSPersistentStoreFileProtectionKey] = FileProtectionType.completeUnlessOpen as AnyObject

        // Options for data migration
        storeOptions[NSMigratePersistentStoresAutomaticallyOption] = true as AnyObject
        storeOptions[NSInferMappingModelAutomaticallyOption] = true as AnyObject

        let _ = try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: dbURL, options: storeOptions)
    }




func generatePrivateContext() -> NSManagedObjectContext {
      // Initialize Managed Object Context
      let managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)

      // Configure Managed Object Context
      managedObjectContext.parent = self.context

      return managedObjectContext
  }
func saveConcurrentContext(privateContext : NSManagedObjectContext) throws {
       privateContext.perform { //Perform childContext  Changes in async thread
           do{
               if privateContext.hasChanges {
                   try privateContext.save()

                   self.context.performAndWait {
                       do{
                           if  self.context.hasChanges{
                               try self.context.save()
                           }
                       }
                       catch (let error){
                          //error
                       }
                   }
               }
           }catch(let error){
              //error
           }
       }
}
}

class DBManager {

        let coreDataStack = CoreDataStack(modelName: "CoreDataModelName")


    func addrecord(){
        guard let privateContext = self.coreDataStack?.generatePrivateContext() else { return  }
        //Added records

        do{
            try coreDataStack?.saveConcurrentContext(privateContext: privateContext)
        }catch
        {

        }
    }

    func deleteRecord(){
        guard let privateContext = self.coreDataStack?.generatePrivateContext() else { return  }
        //delete
          do{
                try coreDataStack?.saveConcurrentContext(privateContext: privateContext)
            }catch
            {

            }
    }

    func updateRecord(){
        guard let privateContext = self.coreDataStack?.generatePrivateContext() else { return  }
        //updateRecord
            do{
                try coreDataStack?.saveConcurrentContext(privateContext: privateContext)
            }catch
            {

            }
    }
}

0
投票

我们知道,在使用并发时,需要使Core Data线程安全。意味着,在插入或删除某些对象时,应防止读取它。这可以通过iOS中的队列来实现。

让我认为模态是:

struct MovieModel: Codable {

   var title           : String?
   var year            : String?
   var imdbID          : String?
   var poster          : String?
}

我希望您知道如何创建核心数据模式类。 Movie + CoreDataProperties类架构包含以下内容:

extension Movie {
   @nonobjc public class func fetchRequest() -> NSFetchRequest<Movie> {
      return NSFetchRequest<Movie>(entityName: "Movie")
   }

   @NSManaged public var imdbID: String?
   @NSManaged public var title: String?
   @NSManaged public var year: String?
   @NSManaged public var poster: String?
}

现在下面的DBManager类在后台执行所有操作。

class DBManager {

/// Share instance of database
static var shared = DBManager()

/// Entity Name
private let MOVIE = "Movie"

private let queue = OperationQueue.init()

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

private var viewContext: NSManagedObjectContext?

/// Making it singleton
private init() {

    self.viewContext = persistentContainer.newBackgroundContext()
}

/// This will add movie object to Movies Entry
///
/// - Parameter movies: array of movies.
public func insert(movies: [MovieModel]) {

    let operation = Operation.init()

    operation.queuePriority = .high

    operation.completionBlock = {

        guard let movieEntity = NSEntityDescription.entity(forEntityName: self.MOVIE, in: self.viewContext!) else { return }

        movies.forEach { (movie) in

            //save movie object if not present in db
            if !self.isAlreadyExist(movieId: movie.imdbID ?? "") {

                let movieObject = NSManagedObject.init(entity: movieEntity, insertInto: self.viewContext!)

                movieObject.setValue(movie.imdbID ?? "", forKey: "imdbID")

                movieObject.setValue(movie.title ?? "", forKey: "title")

                movieObject.setValue(movie.year ?? "", forKey: "year")

                movieObject.setValue(movie.poster ?? "", forKey: "poster")

            }
        }

        do {
            try self.viewContext!.save()
            print("Context saved")
        }
        catch {
            print("Exception while saving contact")
        }

    }

    queue.addOperations([operation], waitUntilFinished: true)
}


/// This will add return the movies those having the provided string in title.
///
/// - Parameter text type: movie title text
public func fetchMovies(contains text: String, completion: @escaping ([MovieModel]) -> Void)  {

    let operation = Operation.init()

    operation.queuePriority = .high

    operation.completionBlock = {

        let fetchRequest = NSFetchRequest<NSFetchRequestResult>.init(entityName: self.MOVIE)

        let predicate = NSPredicate.init(format: "title CONTAINS[cd] %@", text)

        fetchRequest.predicate = predicate

        var movies: [MovieModel] = []

        do {

            let result = try self.viewContext!.fetch(fetchRequest)

            (result as! [NSManagedObject]).forEach { (object) in

                var movie       = MovieModel()

                movie.imdbID = object.value(forKey: "imdbID") as? String

                movie.title = object.value(forKey: "title") as? String

                movie.year = object.value(forKey: "year") as? String

                movie.poster = object.value(forKey: "poster") as? String

                movies.append(movie)
            }
        }
        catch {
            print("Exception while saving contact")
        }


        DispatchQueue.main.async {
            completion(movies)
        }

    }

    queue.addOperations([operation], waitUntilFinished: true)
}


/// This will inform us that the movie object is already present in db or not
///
/// - Parameter movieId: Movie Id to compare
/// - Returns: Boolean flag
private func isAlreadyExist(movieId: String) -> Bool {

    var flag = false

    let fetchRequest = NSFetchRequest<NSFetchRequestResult>.init(entityName: MOVIE)

    let predicate = NSPredicate.init(format: "imdbID == %@", movieId)

    fetchRequest.predicate = predicate

    do {
        let result = try self.viewContext!.fetch(fetchRequest)

        if result.count != 0 {
            flag = true
        }
    }
    catch {
        print("Exception while saving contact")
    }
    return flag

}


/// This will update the movie object in database with provided object
///
/// - Parameter details: movie details
public func updateMovie(with details: MovieModal) {

    let operation = Operation.init()

    operation.queuePriority = .high

    operation.completionBlock = {

        guard let movieId = details.imdbID else { return }

        let fetchRequest = NSFetchRequest<NSFetchRequestResult>.init(entityName: self.MOVIE)

        let predicate = NSPredicate.init(format: "imdbID == %@", movieId)

        fetchRequest.predicate = predicate

        do {
            let result = try self.viewContext!.fetch(fetchRequest)

            if result.count != 0 {

                let movieObject = (result[0] as! NSManagedObject)

                movieObject.setValue(details.imdbID ?? "", forKey: "imdbID")

                movieObject.setValue(details.title ?? "", forKey: "title")

                movieObject.setValue(details.duration ?? "", forKey: "duration")

                movieObject.setValue(details.poster ?? "", forKey: "poster")          

            }
        }
        catch {
            print("Exception while saving context")
        }

        do {
            try self.viewContext!.save()
        }
        catch {
            print("Exception while updating context")
        }
    }
    queue.addOperations([operation], waitUntilFinished: true)
}

}
© www.soinside.com 2019 - 2024. All rights reserved.