我正在开发一个保存一些数据的iPhone应用程序.我正在使用NSKeyedArchiver
类中的归档方法将数据保存到磁盘.我想定期将数据保存到磁盘.问题是,当数据变大时,需要更多时间,实际上会中断用户当前的操作.
因此,我想使用多线程来解决这个问题.根据我对多线程的理解,当我想将数据保存到磁盘时,我应该创建一个新线程,在新线程上运行保存任务,然后终止线程.我也应该创建线程,以便在应用程序终止时不会立即终止,以完成保存数据.这样,用户可以继续与界面交互.
话虽这么说,我不熟悉这些工作的实际代码......上面的代码会是什么样子?
几个想法.
您想要使用串行调度队列或操作队列.
注意,我们可能希望它以串行方式写入持久存储(例如,如果您将其保存到相同的文件名),即不允许在先前的保存完成之前启动另一个保存.我怀疑你的偶然保存不可能在前一个保存仍在进行时触发保存,但作为一般原则,你不应该使用并发队列,除非你编写支持并发操作的代码(我们在这里不做).这意味着您不使用GCD全局队列.
例如,使用Grand Central Dispatch(GCD)创建串行调度队列将是:
@property (nonatomic, strong) dispatch_queue_t queue;
然后实例化(例如viewDidLoad
):
self.queue = dispatch_queue_create("com.domain.app.savequeue", 0);
然后使用此队列
dispatch_async(self.queue, ^{ // do your saving here });
有关并发技术的综述,请参阅" 并发编程指南".调度队列(GCD)和操作队列都是可靠的选择.
您可能需要注意同步问题.如果您的应用在保存期间开始更改数据,该怎么办?这里有很多选项,但最简单的方法是在将保存任务分派到后台队列之前将数据复制到主队列中的某些临时对象:
// copy the model data to some temporary object(s) dispatch_async(self.queue, ^{ // save the temporary object(s) here });
或者,而不是创建模型的副本,则可以选择(这是一个有点复杂,如果你不熟悉GCD)使用苹果讨论了WWDC的"读写器"模式的变体2012视频异步使用Blocks,GCD和XPC设计模式.最重要的是,您可以排队以执行异步写入持久存储,还可以使用"屏障"将更新同步到模型(请参阅GCD参考中的使用障碍):
self.queue = dispatch_queue_create("com.domain.app.modelupdates", DISPATCH_QUEUE_CONCURRENT);
然后,当您想要保存到磁盘时,您可以这样做
dispatch_async(self.queue, ^{ // save model to persistent storage });
但是,每当您想要更新模型时,都应该使用屏障,以便模型的更新不会与任何读取/保存任务同时发生:
dispatch_barrier_async(self.queue, ^{ // update model here });
而且,无论何时从模型中读取,您都会:
dispatch_sync(self.queue, ^{ // read from model here });
从理论上讲,如果您担心可能会经常进行保存操作,以至于当您启动下一个保存操作时仍然可以进行一次保存,那么您实际上可能会使用两个队列,一个串行队列用于保存操作(上面的第1点),以及此处概述的并发队列用于同步过程.
最后,Putz1103是正确的,如果在保存正在进行时可能终止应用程序,您可能希望添加代码以允许写入持久存储完成:
dispatch_async(self.queue, ^{ UIBackgroundTaskIdentifier __block taskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^(void) { // handle timeout gracefully if you can [[UIApplication sharedApplication] endBackgroundTask:taskId]; taskId = UIBackgroundTaskInvalid; }]; // save model to persistent storage // when done, indicate that the task has ended if (taskId != UIBackgroundTaskInvalid) { [[UIApplication sharedApplication] endBackgroundTask:taskId]; taskId = UIBackgroundTaskInvalid; } });