9

想象一下以下情况:你有一个后台任务(这里的“任务”一词是指一个随机计算单元,而不是一个 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

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

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

4

4 回答 4

2

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

从中您实际上可以看到块是实现临时委托的一种更简单的方法,其中块的接收者声明将哪些参数传递给块,并且您可以自由地在块中对它们做任何您想做的事情。而且 KVO 似乎是一种使用更标准化的属性方法来实现 NSNotification 的较少样板的方式,有助于加入以前称为 Interface Bilder 的 UI,并简化“我必须做什么才能知道什么时候这值更改”,这需要大量带有 NSNotification 和长常量的文档。

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

所以我的确定答案是:

  • 1 对 1 简单通信:块
  • 1对1复杂通信:委托/协议
  • 1 对多简单通信:KVO(如果可能)
  • 1对多复杂通信:NSNotifications

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

于 2012-10-04T23:11:42.710 回答
1

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

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

于 2012-10-11T01:13:18.050 回答
0

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

于 2012-10-04T18:38:53.543 回答
0

我会选择 KVO,因为基本上使用 @properties 时您可以免费获得它。但我不建议使用普通的 KVO。因为那总是会调用 -observerValueOfKeyPath... 一旦你观察到多个键路径,维护起来就会很烦人。你有这个带有很多 if(keyPath==bla) 的超级功能......

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

于 2012-11-14T00:26:38.200 回答