如何在iOS中同时从3个不同的线程管理对DB的更新,删除,获取操作?
您可以使用并发线程在核心数据中执行多项操作,例如添加,更新,删除。
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
{
}
}
}
我们知道,在使用并发时,需要使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)
}
}