什么是观察后台任务进度的可可方式?

时间:2012-09-13 09:26:31

标签: multithreading cocoa grand-central-dispatch key-value-observing

想象一下以下情况:你有一个后台任务(术语“任务”在这里意味着一个随机计算单元,而不是NSTask!),它是使用任何现代技术实现的,例如Grand Central Dispatch或Operation Queues。主线程上的某些控制器对象想要监视此后台任务的进度并将其报告给用户。

任务进度可以具有以下特征:

  • 不确定或确定
    因为控制器对象必须知道何时将NSProgressIndicator切换到适当的样式。我们可以使用一种惯例,即将进度视为不确定,直到实际进度值从零增加。
  • 进度值本身
    一个简单的浮点值
  • 当前阶段的本地化描述
    NSString,因为与用户的沟通很好

什么设计最适合这些要求,同时又是最可靠的?

可能有变种。

在启动任务之前,将控制器对象设置为委托。

@protocol MyBackgroundTaskDelegate
@required
- (void) progress: (float) value; // 0.0…1.0
@optional
- (void) workingOn: (NSString*) msg; // @"Doing this, doing that…"
@end

实际上,我多次成功使用过这个模板,但感觉有点过于冗长。

阻止回调

与委托非常相似,但将代码保存在一个地方。

// Starting our background task...
[MyTask startComputationWithProgressHandler: ^(float progress, NSString* msg)
{
    // Switching to the main thread because all UI stuff should go there...
    dispatch_async(dispatch_get_main_queue(), ^()
    {
        self.progressIndicator.progress = progress;
        self.informationalMessage = msg;
    });
}];

KVO或进度属性的轮询

在这种情况下,后台任务对象必须具有两个与此类似的属性:

@property(readonly, atomic) float progress;
@property(readonly, atomic) NSString* message;

客户端(我们的控制器对象)应该将自己设置为这些属性的观察者。我在此解决方案中看到的主要缺陷是KVO通知始终到达导致更改的同一线程。虽然您可以强制您的观察者(回调)方法在特定的GCD队列上运行,但它可能并不总是合适。

NSNotificationCenter

后台任务发送通知,客户端侦听它们。

还有其他适用于这种情况的模式吗?哪种解决方案可以被视为最现代的Cocoa?

4 个答案:

答案 0 :(得分:2)

当谈到观察后台任务进度的Cocoa方法是什么?我会说委托和NSNotificationCenter因为后来引入了块和KVO,因此最初并不存在第一个Cocoa代码写作年。实际上,以前的objc版本中也没有可选的协议方法,默认情况下都需要一切。

由此可以看出,块是实现adhoc委托的一种更简单的方法,块的接收者声明传递给块的参数,并且你可以在中随意做任何你想做的事情。你的阻止。并且KVO似乎是一种用更加标准化的属性方法实现NSNotification的较少的模板方法,对于加入以前称为Interface Bilder创建的UI非常有用,并简化了“我该怎么做才能知道这个值何时改变“,这需要大量的NSNotification和长常量文档。

但是我仍然认为每种技术都有一些地方:块对于迷你adhoc协议很好,但如果你需要一个中等或更高的接口区域或双向接口,那将是一个严重的麻烦,而KVO不会帮助您查看类/对象之外的全局变量或值,或者您不希望成为公共接口的一部分。

所以我的最终答案是:

  • 1对1简单沟通:阻止
  • 1对1复杂通信:委托/协议
  • 1到多个简单的沟通:KVO(尽可能)
  • 1对多复杂的沟通:NSNotifications

与往常一样,为每个问题选择最好的工具,并认为我无法以任何建议的方式实施上述所有内容!

答案 1 :(得分:1)

对于您描述的任务类型,我认为NSNotificationCenter是通用模式的最佳选择。原因是你通常无法知道许多外部观察者的存在方式。通知系统已经支持一个事件的任意数量的观察者,而其他非轮询选项(委托和块)通常是一对一的,除非你做额外的工作来支持多个注册。

正如你自己指出的那样,如果可以避免轮询,那么轮询是一个坏主意。

答案 2 :(得分:0)

根据我的经验,委托或阻止回调是最好的设计选择。选择一个在另一个上面主要取决于哪个更便于编码和支持特定情况。两者都是异步的。块回调通常会减少额外实例变量的必要性,因为块会捕获其范围内的变量。当然,两者都需要知道回调执行的线程或者调用委托方法。

答案 3 :(得分:0)

我会选择KVO,因为基本上使用@properties时你可以免费获得它。 但 我不建议使用普通的KVO。因为它总是会调用 - observerValueOfKeyPath ...并且一旦你观察到多个键路径,它就会很难维护。你有这个巨型函数有很多if(keyPath == bla)......

我推荐MikeAsh的MAKVONotificationCenter。当您不再需要它时忘记移除观察者时,它还可以避免许多崩溃