通知视图更改模型的另一种方法是什么?

时间:2013-09-23 14:37:31

标签: ios objective-c nsnotificationcenter

我有一个简单的应用程序可以使用i / o在2个视图的多个线程中plist文件。所以现在我在plist中有一个带UUID的字段,当我删除该项时 - DataManager通过NSNotificationCenter使用已删除对象的UUID生成消息,

[[NSNotificationCenter defaultCenter] postNotificationName:EADataManagerItemDeleted
                                                            object:nil
                                                          userInfo:userInfo];

所以视图正在侦听消息,如果收到的UUID与视图显示相同 - 系统可以对此作出反应。

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(receiveNotification:)
                                             name:EADataManagerItemDeleted
                                           object:nil];

但是当我们不知道如何以另一种方式获取对象时,我知道我们在非常困难的情况下使用NSNotificationCenter。那么请,我怎么能理解屏幕上的项目在另一个线程中被更改了?谢谢。我的TechLead告诉我为此目的使用NSException类,但无论如何都看不到解决方案。

2 个答案:

答案 0 :(得分:2)

使用KVO(键值观察)。

NSHipster上有一篇精彩而深入的文章:http://nshipster.com/key-value-observing/

使用Cocoa(在桌面上),您可以使用Interface Builder绑定, 在Cocoa Touch(iOS)上,你需要以编程方式使用它,但是一旦你知道如何就相对容易:

简而言之,您可以选择跟踪已知对象的任何属性。在您的情况下,所有这些代码都会进入您的ViewController。我假设(并推荐)您的viewController具有对模型对象(dataManager)的引用。虽然你的模型不能有任何参考回到你的viewController;这就是你将使用Key-Value-Observing的地方。换句话说:您的视图控制器将观察模型属性中的值更改( - > a.k.a“keys”)。

首先,注册KVO:

[dataManager addObserver:self
              forKeyPath:@"nameOfThingIAmInterestedIn"
                 options:NSKeyValueObservingOptionNew
                 context:NULL];

不要忘记删除观察(例如在dealloc中)

[dataManager removeObserver:self forKeyPath:@"nameOfThingIAmInterestedIn"];

最后实现此方法以接收KVO通知。在您的情况下,您应该在主队列上调度dispas_async,以便更新视图(在主线程上):

-(void)observeValueForKeyPath:(NSString *)keyPath 
                     ofObject:(id)object
                       change:(NSDictionary *)change
                      context:(void *)context 
{
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"KVO: %@ changed property %@ to value %@", object, keyPath, change);
    }
}

通知非常相似,但在基于文档的环境中,您可以在应用范围内使用通知,因此它们会跨越文档边界。 KVO是一种更智能,更安全的方式来获取有关已更改属性的通知。

答案 1 :(得分:1)

请,请不要尝试使用NSExceptions查看无效。我甚至不确定这会怎样,但请不要尝试。与其他框架/语言(如Java或.NET)不同,在Cocoa中,异常通常意味着“某些东西已经出现了可怕的错误并且应用程序应该终止”。 (是的,该规则有一些例外,但“查看失效”不是其中之一。)

首先要知道的是UI(即视图)对象通常只被视为主线程。因此,如果您在后台线程上改变模型,则视图的失效应该被封送到主线程。 GCD具有处理此问题的工具(dispatch_get_main_queue),但还有其他工具,例如-[NSObject performSelectorOnMainThread:...]CFRunLoopPerformBlock

接下来要知道的是,如果您要这样做(即在主线程上读取模型以填充UI,并从后台线程更新模型),您将需要某种锁定系统确保在主线程尝试从中读取模型时没有后台线程正在改变模型。您可以在没有锁定的情况下将失效编组到主线程,但是当视图进行重新验证(即布局和绘制)时,您将需要对所表示的模型对象采取适当的锁定以防止数据损坏。管理这些锁定很快就会变得非常重要。 (尽管使用dispatch_(a)sync& dispatch_barrier_(a)sync访问的私有并发GCD队列是一个非常好的起点。)

避免大多数锁定复杂性的一种常见模式是让后台线程在模型的副本上完成工作,然后不仅发送视图失效,还发送模型变异操作到主线程,所以就“一个真正的模型”来说,它总是只有主线程,在这种情况下你不需要担心锁定。如果你能以这种方式做事,那将会给你带来很多悲伤。

在OSX(但不是iOS)上有Cocoa绑定,它使用键值观察(其间有一堆其他不透明的“魔法”)在模型更改时自动使绑定的UI无效和更新。 iOS上不存在Cocoa绑定,但值得一提的是KVO 并且它发送的通知可用于查看失效。重要的是要知道KVO通知是在发生突变的线程上同步发送的,所以再一次,你想要将你的突变编组回主线程。

最后,使用NSNotificationCenter并不会让您免于担心锁定和跨线程通知,因为与KVO一样,NSNotifications在发布它们的线程上同步传递。在具有UIViews观察者的后台线程上发送NSNotification也会引起问题。