在objective-c中从两个不同进程写入资源的最佳实践

时间:2013-03-27 22:38:20

标签: ios objective-c design-patterns asynchronous

我有一个关于我试图用我的应用程序解决的问题的一般目标-c模式/练习问题。我在这里找不到类似的以客观为中心的问题/答案。

我的应用程序拥有一个可变数组的对象,我称之为“记录”。该应用程序收集记录并以两种方式之一将它们放入该数组:

  1. 它从应用程序沙箱中本地可用的SQLite数据库中读取数据。阅读通常非常快。
  2. 它从Web服务异步请求数据,等待它完成然后解析数据。读取可以很快,但通常不是。
  3. 有时应用程序从数据库(1)读取并在几乎同时从Web服务(2)请求数据。通常的情况是(1)将在(2)完成之前完成并且将记录添加到可变数组不会引起冲突。

    我担心在某些时候我的SQLite读取过程会花费比预期更长的时间,它会尝试在异步请求完成的同时将对象添加到可变数组中并执行相同操作;或相反亦然。这些边缘情况似乎难以测试,但肯定会使我的应用程序崩溃或至少导致我的记录数组出现问题。

    我还应该指出,记录将被合并到可变数组中。例如:if(1)先运行并返回10条记录,然后在(2)完成并返回5条记录后不久,我的可变数组将包含所有15条记录。我正在组合数据而不是覆盖它。

    我想知道的是:

    • 当进程(1)或(2)完成时,将对象添加到同一个可变数组实例是否安全?
    • 在objective-c中进行这种处理是否有良好的模式/实践?
    • 这是否涉及锁定对可变数组的访问权限,以便在(1)向其添加对象时(2)在(1)完成之前不能添加任何对象?

    感谢您分享的任何信息。

    [编辑#1]

    对于后人,我发现这个URL对于理解如何使用NSOperations和NSOperationQueue非常有帮助。它有点过时了,但仍然有效:

    http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues

    此外,它没有具体谈到我正在尝试解决的问题,但它使用的示例实用且易于理解。

    [编辑#2]

    我决定采用danh建议的方法,我会在本地阅读,并在本地阅读结束后根据需要点击我的网络服务(无论如何应该很快)。 Taht说,我将尝试完全避免同步问题。为什么?因为Apple这样说,在这里:

    http://developer.apple.com/library/IOS/#documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8-SW8

      

    完全避免同步

         

    对于您正在处理的任何新项目,甚至是现有项目,设计代码和数据结构以避免同步是最佳解决方案。虽然锁和其他同步工具很有用,但它们确实会影响任何应用程序的性能。如果整体设计导致特定资源之间存在高争用,那么您的线程可能会等待更长时间。

         

    实现并发的最佳方法是减少并发任务之间的交互和相互依赖关系。如果每个任务都在自己的私有数据集上运行,则不需要使用锁保护该数据。即使在两个任务共享公共数据集的情况下,您也可以查看设置分区的方法或为每个任务提供自己的副本。当然,复制数据集也有其成本,因此在做出决定之前,您必须权衡这些成本与同步成本。

4 个答案:

答案 0 :(得分:2)

  

当进程(1)或(2)完成时,将对象添加到同一个可变数组实例是否安全?

绝对不是。 NSArray以及其他集合类are not synchronized。当你添加和删除对象时,你可以将它们与某种锁结合使用,但这肯定比制作两个数组(每个操作一个数组)慢,并且当它们都完成时合并它们。

  

在objective-c中实现这种处理是否有良好的模式/实践?

不幸的是,没有。你能想到的最多就是跳过布尔值,或者在一个公共回调中将整数递增到一定数量。要明白我的意思,这里有一些伪代码:

- (void)someAsyncOpDidFinish:(NSSomeOperation*)op {
    finshedOperations++;
    if (finshedOperations == 2) {
       finshedOperations = 0;
       //Both are finished, process
    }
}
  

这是否涉及锁定对可变数组的访问权限,以便在(1)向其添加对象时(2)在(1)完成之前不能添加任何对象?

是的,见上文。

答案 1 :(得分:2)

您应该锁定数组修改,或者在主线程中安排修改。 SQL提取可能在主线程中运行,因此在远程提取代码中,您可以执行以下操作:

dispatch_async(dispatch_get_main_queue(), ^{
    [myArray addObject: newThing];
}];

如果要添加一堆对象,这将会很慢,因为它会在每个记录的调度程序上放置一个新任务。您可以将记录捆绑在线程中的单独数组中,并使用addObjectsFromArray添加临时数组:如果是这种情况。

答案 2 :(得分:1)

就个人而言,我倾向于拥有并发NSOperationQueue并添加两个检索操作操作,一个用于数据库操作,一个用于网络操作。然后,我将有一个专用的串行队列,用于将记录添加到NSMutableArray,两个并发检索操作中的每一个都将用于向可变数组添加记录。这样你就有一个队列来添加记录,但是从另一个并发队列上运行的两个检索操作中获取。如果您需要知道两个并发检索操作何时完成,我将向该并发队列添加第三个操作,将其依赖关系设置为两个检索操作,这两个检索操作将在两个检索操作完成时自动触发。

答案 3 :(得分:1)

除了上面提到的好建议外,请考虑不要同时启动GET和sql。

[self doTheLocalLookupThen:^{
    // update the array and ui
    [self doTheServerGetThen:^{
        // update the array and ui
    }];
}];


- (void)doTheLocalLookupThen:(void (^)(void))completion {

    if ([self skipTheLocalLookup]) return completion();
    // do the local lookup, invoke completion
}

- (void)doTheServerGetThen:(void (^)(void))completion {

    if ([self skipTheServerGet]) return completion();
    // do the server get, invoke completion
}