延迟后在后台线程中处理核心数据
Posted
技术标签:
【中文标题】延迟后在后台线程中处理核心数据【英文标题】:Process Core Data in background thread after a delay 【发布时间】:2015-07-28 07:29:25 【问题描述】:我有一个表格视图来显示从 API 获取的列表。
首先,我将 API 数据存储到 Core Data,然后在后续应用启动时,我将尝试更新列表。
我需要在后台线程中处理更新操作。应该在启动特定 ViewController 5 秒后调用更新操作
func updateGlossary()
var GlobalMainQueue: dispatch_queue_t
return dispatch_get_main_queue()
let delayInSeconds = 5.0
let popTime = dispatch_time(DISPATCH_TIME_NOW,
Int64(delayInSeconds * Double(NSEC_PER_SEC)))
dispatch_after(popTime, GlobalMainQueue)
self.showAlertMessage(message: "Updating Glossary")
DataStore.GetToken( (token, error) in
//Got Token
//Call the API for updated data and store it to core data
)
这里面有两个问题;
-
进行更新时 UI 被阻止
当我在更新操作开始之前从控制器返回时,它仍然会从其他控制器更新(我认为问题是因为排队)
我正在使用 Swift、Alamofire、Core Data
【问题讨论】:
【参考方案1】:第一个问题:-
更新进行时用户界面被阻止
你应该以异步方式调用updateGlossary操作,它不会阻塞主线程:-
dispatch_async( dispatch_get_global_queue(0, 0), ^
[self performSelector:@"updateGlossary" withObject:nil afterDelay:5.0]
);
第二个问题:-
当我在更新操作开始之前从控制器返回时,它仍然会从其他控制器更新(我认为问题是因为排队)
只需在视图控制器的 viewDidDisapper() 方法中停止线程
-(void)viewDidDisappear:(BOOL)animated
[super viewDidDisappear:FALSE];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(updateGlossary) object:nil];
【讨论】:
【参考方案2】:不,不要使用dispatch_async
进行 CoreData 操作。 NSManagedObjectContext
本身不是线程安全的,会导致意外行为。你可能仍然使用dispatch_after
来调用主线程上的块。
您的代码很好,只是您的 getToken(:_)
可能发生在主线程上和/或您的数据库操作发生在主线程上,因此阻塞了 UI。
你可以像这样修改你的代码:
func updateGlossary()
var GlobalMainQueue: dispatch_queue_t
return dispatch_get_main_queue()
let delayInSeconds = 5.0
let popTime = dispatch_time(DISPATCH_TIME_NOW,
Int64(delayInSeconds * Double(NSEC_PER_SEC)))
dispatch_after(popTime, GlobalMainQueue)
self.showAlertMessage(message: "Updating Glossary")
DataStore.GetToken( (token, error) in
//Got Token
//This will execute in the background
self.callAPIWithToken(token, success: (data) -> () in
//Callback on the main thread
self.privateManagedObjectContext.performBlock
//Will execute in its private thread
//Insert to the database, save context
) (error) -> () in
)
privateManagedObjectContext
是NSManagedObjectContext
的一个实例,其并发类型为.PrivateQueueConcurrencyType
,其父存储为.MainQueueConcurrencyType
。您使用privateManagedObjectContext
在后台插入并保存您的数据。这种方法是线程安全的,不会阻塞您的 UI。
Here 是一篇关于多上下文核心数据系统的好文章。
编辑
如果您想在离开时取消,我不建议使用dispatch_after
。请改用NSTimer
。让它在 5 秒后在主线程上进行服务调用;当您离开控制器时使其无效。
【讨论】:
var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as!AppDelegate) var context: NSManagedObjectContext = appDel.managedObjectContext!这是我的上下文,当我使用它时,出现错误 NSInvalidArgumentException',原因:'Can only use -performBlock: on an NSManagedObjectContext that was created with a queue.' 正确。这是因为您使用的上下文是.ConfinementQueueConcurrencyType
类型。请通读我与文章链接的文章。这个方法对你来说会更清楚。
效果很好,但是如何在离开控制器时取消排队的任务
是取消请求还是取消数据库插入?【参考方案3】:
UI 被阻塞,因为您在 mainQueue 上进行 API 调用。创建一个不同的队列并进行 API 调用,稍后在您到达主队列后将 API 数据插入 Coredata。
我可以从您的代码中看到您正在使用GlobalMainQueue
,它是dispatch_get_main_queue
的别名,因此您的 UI 卡住了。
解决方法如下:
var GlobalBackgroundQueue: dispatch_queue_t
return dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.value), 0)
dispatch_after(popTime, GlobalBackgroundQueue)
//Make the API Call
dispatch_async(dispatch_get_main_queue())
//Insert Into CoreData
【讨论】:
以上是关于延迟后在后台线程中处理核心数据的主要内容,如果未能解决你的问题,请参考以下文章