在Objective-C中绑定模型类的方法

时间:2011-09-29 09:41:20

标签: iphone objective-c model-view-controller key-value-observing nsnotifications

(如果我弄错了,我希望有人能纠正我的术语 - 我还在整理条款)

我的模型中有一系列的课程。我需要从网址(SatDataGetter)获取一些数据并将其与位置和日期特定计算(DayCalculater)相结合,进行一些进一步的计算(DataMixer),一些解释以使其易于用户(Advisor),然后呈现结果在视图中。

设置依赖项并确保例如SatDataGetter在之前有效数据由DataMixer调用之前之前调用..你明白了。当然,如果位置发生变化,我需要从下往上更新整个过程。至少我必须向ViewController和Advisor发送一条消息来重新加载他们的数据。

我所做的研究表明,NSNotification是一种可行的方法,但我也可以尝试使用Key-Value Observing。我在KVO上找到了一些旧帖子(2009),暗示了一些可能的问题和调试方面的困难。 http://www.mikeash.com/pyblog/key-value-observing-done-right.html

首选方法是什么?在决定时我应该考虑哪些问题 - 例如: SatDataGetter基本上返回一个数字。 KVO似乎是DataMixer跟踪该值的合理方式,但我认为我不希望所有父类对因变量进行KVO。

您何时选择NSNotification和KVO?

4 个答案:

答案 0 :(得分:3)

NSNotifications和Key-Value Observation之间的区别主要是耦合,但也有性能影响。

任何人都可以订阅您的NSNotifications。他们需要知道的是你通知的字符串/密钥。他们不需要知道关于你的类/对象图等的任何信息。所以当你想要通知一个不了解你的课程细节的世界时,NSNotification就是你要走的路。例如,如果您向其他开发人员出售框架,那么通过NSNotification进行通知可能比通过将框架内部暴露到允许消费者使用Key-Value观察对象所必需的程度更好。

为了让KVO观察一个物体,你首先必须能够得到它的参考,这对NSNotifications来说并不严格(但在我的经验中通常都是如此)。其次,你需要了解足够多的信息。实现知道要观察什么。使用NSNotification,通知程序只需要发布通知字符串/密钥。使用KVO,您需要知道对象属性的名称。当然,有人可以发布静态字符串并告诉你“你可以为我提供这些属性”,但这实际上会成为一个可能更难维持的API合同。 (例如,你想在未来版本中删除该属性 - 然后你必须安排其他事情继续发送这些通知并在人们调用valueForKey时提供值: - 简而言之,一旦你这样做,你永远不能改变那个属性。)

使用这些不同程度的耦合要记住的另一件事是,对于KVO,可以期望观察者知道类/对象的细节。毕竟,他们正在对你的对象注册一个非常特殊的兴趣;他们声称知道这意味着什么 - 它是如何运作的。因此,您可能希望它们对性能影响敏感。通过NSNotifications,消费者可以在几乎不了解您的情况下观察您的通知,并且可能不了解他们如何响应通知的性能影响。

这两种方法共有的缺点是,如果没有额外的工作,它们都是同步传递的。通知对象受到观察者在收到通知时选择(同步)所做的事情的摆布。这两种机制的不同之处在于,对于NSNotification,通知对象很容易使用performSelector:afterDelay:导致通知在下一次运行时“异步”(相对于产生通知的调用)发送循环(另请参阅NSNotificationQueue)。 KVO不太容易实现这一点。在某些情况下,这种差异本身可能至关重要。

通常,我发现NSNotification的松散耦合适用于大颗粒(即可能代表一大组变化)或相对不频繁的事件。 KVO通知本质上是细粒度的。您明确地观察单个对象上的单个属性(每个注册)的更改。这有可能炸毁注册数量和通知数量。每次观察都会产生性能成本。

这两者的另一个共同缺点是可调试性。我在上面的评论中提到KVO可能是调试的挑战,它也与NSNotificationCenter分享了这一挑战。

这两者和委托模式之间的主要区别在于委托关系通常是1:1(一个委托。)(当然,你可以有一个委托数组,但这是非常罕见的,并且可辩解地是反模式。 )NSNotification和KVO本身就是1:N(许多观察者)。

最后,我总是喜欢说,“使用最高级别的抽象来完成工作。”如果您只需要1:1的关系,请使用委托。对于1:N通知,如果NSNotification将起作用 - 换句话说,如果所需的耦合较低和/或通知是大粒度或不常见的,请使用NSNotifications。如果联轴器紧固,或需要细粒度,请使用KVO。还要记住,通过让委托发送NSNotifiations,您可以将委托模式从1:1调整为1:N。使用NSNotifications和KVO,您无法真正控制观察您的人数或您拥有的观察者数量。

答案 1 :(得分:0)

我通常使用KVO来监视对象内属性的更改 - 因此,我将使用KVO在更改时触发observe方法,然后在此时调用其他方法,而不是覆盖对象的setter。 。

我倾向于使用NSNotifications来通知其他对象已发生的事情。在您的示例中,我可能有一个单例来处理位置内容,然后在userLocation更改时发送通知。

当然,您也可以使用委托模式向另一个对象通知更改。

这些并不是硬性规定的快速规则......我不会随意改变!

答案 2 :(得分:0)

您可能不需要任何这些策略,您可以使用某种委托层次结构。如果您使用核心位置或任何其他无法同步获取可靠数据的服务,那么您可以将其作为起点...如果您有2个此类服务,(例如获取http数据和核心位置) ),然后你可能必须执行一些缓存和委托...即最后的最佳位置+当前的http数据被推送到视图。

答案 3 :(得分:0)

NSOperations怎么样?

// create two ops, and set a dependency
DownloadOp *op = [[Download alloc] initWithURL:@"http://xxx"];
CalculationOp *op = [CalculationOp new];
[calculationOp addDependency:downloadOp];
// ... more steps

// add to the queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:downloadOp];
[queue addOperation:calculationOp];

// runloop for unit testing
while(!parserOp.isFinished) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}

-[CalcuationOp start]

    // get the result from the previous step
    id obj = [self.dependencies objectAtIndex:0];
    if ([obj isKindOfClass:[DownloadOp class]]){
        DownloadOp *op = (DownloadOp*) obj;
        self.input = op.output;
    }

AdvisorOp内部:

  // update the GUI from the main thread
  dispatch_queue_t mainQueue = dispatch_get_main_queue();
  dispatch_async(mainQueue,^{
    // update some UI element
 });

然后在CLLocationManager委托上监听位置更新以启动整个事情。我正在使用这个与操作分离的逻辑,所以我可以单独测试每个部分 好处:它以异步方式运行,您可以随时取消队列上的操作。缺点:你必须学习NSOperation。