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 进行多线程处理? [关闭]