CoreData 阻塞 UI
Posted
技术标签:
【中文标题】CoreData 阻塞 UI【英文标题】:CoreData blocking UI 【发布时间】:2016-09-15 10:43:37 【问题描述】:我正在开发一个使用 CoreData 的现有项目:
在从 Web 服务接收到来自不同 CoreData 实体类型的许多项目后,它会阻塞 UI 线程数秒,即使我在另一个线程中使用它也是如此。
请帮帮我,有什么办法可以防止 CoreData 在项目快完成时以最少的更改阻塞 UI?
我是CoreData
的新手,很遗憾,我没有足够的时间研究文档或重新编写源代码。
我的数据控制器:
class DataController
var managedObjectContext: NSManagedObjectContext
let modelName = "something"
init(closure:()->())
guard let modelURL = NSBundle.mainBundle().URLForResource(modelName, withExtension: "momd"),
let managedObjectModel = NSManagedObjectModel.init(contentsOfURL: modelURL)
else
fatalError("DataController - COULD NOT INIT MANAGED OBJECT MODEL")
let coordinator = NSPersistentStoreCoordinator.init(managedObjectModel: managedObjectModel)
managedObjectContext =
let parentContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
parentContext.persistentStoreCoordinator = coordinator
let managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.parentContext = parentContext
return managedObjectContext
()
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.rawValue), 0))
let options = [
NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true,
NSSQLitePragmasOption: ["journal_mode": "DELETE"]
]
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last
let storeURL = NSURL.init(string: "something", relativeToURL: documentsURL)
do
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: options)
dispatch_async(dispatch_get_main_queue())
closure()
catch let error as NSError
fatalError("DataController - COULD NOT INIT SQLITE STORE: \(error.localizedDescription)")
func save(moc: NSManagedObjectContext? = nil)
var managedObjectContext = moc
if moc == nil
managedObjectContext = self.managedObjectContext
managedObjectContext!.performBlockAndWait
if managedObjectContext!.hasChanges
do
try managedObjectContext!.save()
catch
print("ERROR saving context \(managedObjectContext!.description) - \(error)")
if let parentContext = managedObjectContext!.parentContext
self.save(parentContext)
我的课程是这样的:
class MOCity: NSManagedObject
@NSManaged var id: NSNumber?
@NSManaged var name: String?
// Insert code here to add functionality to your managed object subclass
class func save(id: NSNumber, json: JSON, managedObjectContext: NSManagedObjectContext)
guard let newObject = MOCity.getById(id, create: true, managedObjectContext: managedObjectContext) else
fatalError("City - NO OBJECT")
newObject.id <-- json["Id"]
newObject.name <-- json["Name"]
internal class func getById(id: NSNumber, create: Bool = false, managedObjectContext: NSManagedObjectContext) -> MOCity?
let fetchRequest = NSFetchRequest(entityName: "City")
fetchRequest.predicate = NSPredicate(format: "id = \(id)")
do
guard let fetchResults = try managedObjectContext.executeFetchRequest(fetchRequest) as? [MOCity] else
fatalError("city - NO FETCH RESULTS")
if fetchResults.count > 0
return fetchResults.last
else if fetchResults.count == 0
if create
guard let object = NSEntityDescription.insertNewObjectForEntityForName("City", inManagedObjectContext: managedObjectContext) as? MOCity else
fatalError("getCity - COULD NOT CREATE OBJECT")
return object
else
return nil
catch let error as NSError
...
return nil
还有我的网络服务类:
Alamofire.request(.POST, url,parameters:data).validate().responseJSON(completionHandler: response in
switch response.result
case .Success:
if let jsonData = response.result.value
if let array = jsonData as? NSArray
for arrayItem in array
MOCity.save(arrayItem["Id"] as! NSNumber, json: JSON(arrayItem as! NSDictionary)!, managedObjectContext: self.dataController.managedObjectContext)
self.dataController.save()
break
case .Failure(let error):
...
)
【问题讨论】:
【参考方案1】:其中一个问题可能与 QOS 级别有关。
QOS_CLASS_USER_INITIATED:用户发起的类代表任务 从 UI 启动并且可以异步执行。它 应在用户等待即时结果时使用,并且 继续用户交互所需的任务。
尝试使用QOS_CLASS_BACKGROUND
。
【讨论】:
我尝试使用它,但它导致核心数据操作崩溃。请问有什么解决方案或示例代码吗? 谢谢,我会等待你的帮助。 :) 当应用程序第一次启动时,我会一起调用一个请求列表,所有这些都是我在这里写的。 我使用 alamofire 从其他线程调用 Web 服务请求 然后在响应时我解析并在 UI 线程上使用它们,但我不知道如何在另一个线程上正确使用 CoreData【参考方案2】:我建议两件事。首先,将私有队列上下文保存在 performBlock 函数中,就像保存主队列上下文一样。其次,在不同的地方设置断点,例如在保存之前和之后,以检查哪个线程正在调用该代码(使用 NSThread 的线程检查方法)。这可能会启发您了解从哪个线程执行的代码。希望这有助于引导您朝着正确的方向前进。
【讨论】:
以上是关于CoreData 阻塞 UI的主要内容,如果未能解决你的问题,请参考以下文章
CoreData从后台线程读取数据仍然阻塞UI界面的原因及解决
写入 coredata,即使在后台线程上完成,也会严重阻塞 UI