1

我有一个简单的应用程序可以使用 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 类,但无论如何都看不到解决方案。

4

2 回答 2

2

使用 KVO(键值观察)。

NSHipster 上有一篇很好且深入的文章:http: //nshipster.com/key-value-observing/

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

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

首先,注册KVO:

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

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

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

最后实现这个方法来接收 KVO 通知。在您的情况下,您应该在主队列上 dispatch_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 是一种更智能、更安全的方式来获取有关更改的属性的通知。

于 2013-11-29T13:04:10.453 回答
1

请,请,请不要尝试NSExceptions用于视图失效。我什至不确定这将如何工作,但请不要尝试。与其他框架/语言(如 Java 或 .NET)不同,在 Cocoa 中,异常通常意味着“出现严重错误,应用程序应该终止”。(是的,该规则有一些例外,但“视图失效”不是其中之一。)

首先要知道的是 UI(即视图)对象通常仅被视为主线程。因此,如果您在后台线程上更改模型,则应将视图的失效编组到主线程。GCD 有处理这个 ( dispatch_get_main_queue) 的工具,但也有其他工具,例如-[NSObject performSelectorOnMainThread:...]CFRunLoopPerformBlock

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

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

在 OSX(但不是 iOS)上有 Cocoa 绑定,它使用 Key-Value Observing(中间有一堆其他不透明的“魔法”)在模型更改时自动失效和更新绑定的 UI。iOS 上不存在 Cocoa 绑定,但值得一提的是,KVO存在,并且它发送的通知可用于视图失效。重要的是要知道 KVO 通知是在发生突变的线程上同步发送的,所以同样,您希望将您的突变编组回主线程。

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

于 2013-09-23T14:54:29.580 回答