Core Data 默认使用线程吗?

Posted

技术标签:

【中文标题】Core Data 默认使用线程吗?【英文标题】:Does Core Data Use Threading By Default? 【发布时间】:2016-03-12 14:34:23 【问题描述】:

我正在考虑使用 coreData 来存储我的用户名和密码,以便用户持久保存。在测试时,我注意到当我在保存数据后快速关闭程序并重新启动它以检查数据是否持久存在时,它有时会说什么都不存在,然后当我再次重新启动应用程序时它就会在那里。当我重新启动应用程序时,我等待的时间越长,持久化数据出现的可能性就越大。

我正在将 coreData 添加到现有项目中,因此我创建了一个名为 DataController.swift 的控制器并从 Apple 复制了建议的代码。代码如下。

import UIKit
import CoreData

class DataController: NSObject 
    var managedObjectContext: NSManagedObjectContext
    override init() 
        // This resource is the same name as your xcdatamodeld contained in your project.
        guard let modelURL = NSBundle.mainBundle().URLForResource("AppSettings", withExtension:"momd") else 
            fatalError("Error loading model from bundle")
        
        // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
        guard let mom = NSManagedObjectModel(contentsOfURL: modelURL) else 
            fatalError("Error initializing mom from: \(modelURL)")
        
        let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)
        self.managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
        self.managedObjectContext.persistentStoreCoordinator = psc
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) 
            let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
            let docURL = urls[urls.endIndex-1]
            /* The directory the application uses to store the Core Data store file.
            This code uses a file named "DataModel.sqlite" in the application's documents directory.
            */
            let storeURL = docURL.URLByAppendingPathComponent("AppSettings.sqlite")
            do 
                try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil)
             catch 
                fatalError("Error migrating store: \(error)")
            
        
    

我的 LoginViewController.swift 如下:

import UIKit
import CoreData

class LoginViewController: UIViewController, UITextFieldDelegate 

@IBOutlet weak var usernameField: UITextField!
@IBOutlet weak var passwordField: UITextField!

let moc = DataController().managedObjectContext

@IBAction func SignUpButtonPressed(sender: UIButton) 
    print("sign up")


func textFieldShouldReturn(textField: UITextField) -> Bool 
    textField.resignFirstResponder()
    return true


func textFieldShouldEndEditing(textField: UITextField) -> Bool 
    textField.resignFirstResponder()
    return true



override func viewDidLoad() 
    super.viewDidLoad()

    let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
    view.addGestureRecognizer(tap)

    print("view loaded, check if already signed in here")

    let loggedIn = checkIfLoggedInAlready() //checks database to see

    if(loggedIn)
        print("was logged in!")
    


func checkIfLoggedInAlready() -> Bool
    let fetchRequest = NSFetchRequest(entityName: "AppSettings")
    //let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) //Deletes ALL appsettings entities

    do 

        let fetchRequest = try moc.executeFetchRequest(fetchRequest) as! [AppSettings]

        guard let appSettingsArrayItem = fetchRequest.first where fetchRequest.count>0 else 
            print ("no entities found...")
            return false
        

        guard let username = (appSettingsArrayItem as AppSettings).username else
            print ("username not found")
            return false
        

        print("number Of AppSetting Entities =\(fetchRequest.count)")
        print(username)

        //The following code deletes ALL the entities!
        //try moc.persistentStoreCoordinator!.executeRequest(deleteRequest, withContext: moc)

        //To delete just '1' entry use the code below.

        //moc.deleteObject(appSettingsArrayItem)
       // try moc.save()//save deletion change.

        print("deleted particular entity item")

        return true
     catch
        fatalError("bad things happened \(error)")
    




func dismissKeyboard() 
    //Causes the view (or one of its embedded text fields) to resign the first responder status.
    view.endEditing(true)


override func didReceiveMemoryWarning() 
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.


override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) 
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
    print("prepar seque")


func displayErrorMessage(errorMessage: String)
    print("show error console with Error:"+errorMessage)
    let alert = UIAlertController(title: "Error", message: errorMessage, preferredStyle: UIAlertControllerStyle.Alert)
    alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
    self.presentViewController(alert, animated: true, completion: nil)


override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool 
    switch(identifier)
        case "loginSegue":

            print("loginSegue Check")

            guard let password = passwordField.text!.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet()) where !password.isEmpty else 
                displayErrorMessage("Password can not be empty!")
                return false
            

            guard let username = usernameField.text!.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet()) where !username.isEmpty else
                displayErrorMessage("Username can not be empty!")
                return false
            

            let url = "http://mywebsite.com/restapi/v1/userlogin?email="+username+"&password="+password
            print(url)

            let json = JSON(url:url)
            print(json)

            if(json["status"].asInt==1)

                let entity = NSEntityDescription.insertNewObjectForEntityForName("AppSettings", inManagedObjectContext: moc) as! AppSettings

                entity.setValue(username, forKey: "username")
                entity.setValue(password, forKey: "password")
                entity.setValue(json["tokenid"].asString, forKey: "token")
                entity.setValue(json["roleid"].asInt, forKey: "roleid")
                entity.setValue(json["role"].asString, forKey: "role")
                entity.setValue(json["companyid"].asInt , forKey: "companyid")
                entity.setValue(json["isdev"].asInt, forKey: "isdev")

                //save token and other details to database.
                do 
                    try moc.save()
                    print("saved to entity")
                catch
                    fatalError("Failure to save context: \(error)")
                

                //token
                //roleid int
                //role
                //companyid int



                return true //login succesfull
            else
                displayErrorMessage("Incorrect Username or Email")
                return false//failed
            

    default:
        displayErrorMessage("Unknown Error Related To Segue Not Found")
    
  return false //if it gets to this point assume false


要测试我的代码,您只需取消注释函数 checkIfLoggedInAlready() 中删除实体 (//moc.deleteObject(appSettingsArrayItem) ) 的部分。

简而言之,虽然这个问题的答案很简单,是或否,但我有一种预感,这就是延迟很重要的原因。我认为它的线程是因为 DataController.swift 中的这一行

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0))

但不确定这是否是影响它的原因。创建实体的代码在 LoginViewController.swift 的 shouldPerformSegueWithIdentifier 中完成

【问题讨论】:

仅供参考,使用 CoreData 存储用户凭据是个坏主意,它不够安全。我绝对建议您使用钥匙串来存储数据。你可以在这里阅读更多相关信息:josee.me/2012/03/16/core-data-passwords-in-keychain 感谢您的帮助,不知道,一定会查一下。 【参考方案1】:

默认情况下不是。所有的 NSManagedObjects 都绑定到 NSManagedContext ,它有自己的队列。如果你想要并发,你需要创建另一个带有私有队列的 NSManagedContext。您可以在此处阅读有关 Core Data 中的并发性:

https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Concurrency.html

另外,切勿将dispatch_async 用于Core Data。每个上下文都有它自己的peformBlock,它在它的队列上运行。

【讨论】:

我的命令有类似的问题,让 fetchRequest = try moc.executeFetchRequest(fetchRequest) as! [AppSettings],(它与我上面列出的代码相同)似乎有时它会获取实体,有时它不会。大多数时候它会获取,但有时不会。有什么办法可以保证它会吗? (或者至少给它时间真正正确地运行它的代码

以上是关于Core Data 默认使用线程吗?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Core Data 从后台线程获取

如何正确使用 Core Data 进行多线程处理? [关闭]

Core Data 托管对象上下文线程同步

如何在 asp.net Core 中增加最大线程池限制

好的教程或适合在 IOS 7 中使用 Core.Data [关闭]

如何使用 Core Data 有效地保存 UI/主线程中所做的更改?