3

我正在寻找一种可靠的设计来处理涉及异步请求的分配。为了进一步澄清,我有一个处理数据管理的类。它是一个单例,包含很多对我来说的顶级数据,这些数据在我的 iPhone 应用程序中使用。

视图控制器可能会执行以下操作:

users = [MySingleton sharedInstance].users;

然后 MySingleton 将覆盖合成的用户 getter 并查看它是否已设置。如果未设置,它将与触发异步请求的连接管理器(NSURLConnection 及其委托方法的包装器)对话,这就是问题开始的地方。我无法保证“用户”何时可用。我可以将请求更改为同步,但这会直接影响用户体验,尤其是在带宽已经有限的移动环境中。

我需要能够在某个时候在我的 getter 中进行某种锁定/同步代码,直到它可用或为零时才会返回用户。

一旦 NSURLConnection 有可用的数据,它需要用响应对象回调某处/某处,并让 getter 知道数据可用..无论它是失败还是成功。

关于处理这个有什么建议吗?

4

3 回答 3

3

我在不同的应用程序中通过几种方式解决了这个问题。

一种解决方案是传递一个对象和选择器来通知,例如:

- (id)getUsersAndNotifyObject:(id)object selector:(SEL)selector

然而,这打破了良好的属性行为。如果要将方法保留为属性,请让它们立即返回,使用缓存数据或 nil。如果您需要上网,请异步执行此操作,然后让应用程序的其余部分知道通过 KVO 或 NSNotificationCenter 更改的数据。(可可绑定将是 Mac 上的一个选项,但它们在 iPhone 上不存在)。

这两种方法非常相似。使用您的共享实例注册更新,然后请求数据。如果您只处理原始的可观察属性,KVO 的重量会稍微轻一些,但是如果您对几个不同的数据感兴趣,则 NSNotification 可能会更方便。

使用 NSNotification,客户端对象可以注册一种类型的通知,其中包括其 userInfo 字典中的更改数据,而不必为您感兴趣的每个键路径注册观察服务器。

与直接 KVO 相比,NSNotification 还允许您更轻松地回传故障或其他状态信息。

KVO方法:

// register observer first so you don't miss an update
[[MySingleton sharedInstance] addObserver:self
                               forKeyPath:@"users"
                                  options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
                                  context:&kvo_users_context];
 users = [MySingleton sharedInstance].users;

 // implement appropriate observeValueForKeyPath:ofObject:change:context: method

NSNotification 方法:

 [[NSNotificationCenter defaultCenter] addObserver:self
                                          selector:@selector(sharedDataChanged:)
                                              name:MySingletonDataUpdatedNotification
                                            object:[MySingletonDataUpdatedNotification sharedInstance]];
 users = [MySingleton sharedInstance].users;

 // implement appropriate sharedDataChanged: method
于 2008-12-16T03:15:22.647 回答
1

您可以在此处使用委托模式或通知模式。

当用户完成时,委托会通知特定对象,通知模式会通知任何想知道的对象。两者都有效,具体取决于您的情况。

请记住:如果您的应用程序中有任何种族问题,那么您的架构可能全都错了。

于 2008-12-15T21:51:13.833 回答
0

我花了一段时间才意识到处理这种典型任务的最佳方式是什么;事实证明,线索在于许多 Cocoa 和 CocoaTouch 自己的 API 的设计:委托

很多 Cocoa 的 API 使用委托的原因是因为它非常适合许多 GUI 应用程序的异步特性。

想要做一些类似的事情似乎是完全正常的:

users = [MyDataFactory getUsers];

除了,正如您所指出的,您不知道该getUsers方法何时完成。现在,有一些轻量级的解决方案;amrox在上面的帖子中提到了一些(我个人认为通知不太合适,但 object:selector: 模式是合理的),但如果你经常做这种事情,委托模式往往会产生一个更优雅的解决方案。

我将尝试通过一个示例来解释我如何在我的应用程序中做事。

假设我们有一个域类,Recipe. 食谱是从 Web 服务中获取的。我通常有一系列存储库类,一个用于我模型中的每个实体。存储库类的职责是获取实体(或它们的集合)所需的数据,使用该数据构造对象,然后将这些对象传递给其他对象以使用它们(通常是控制器或数据源)。

我的RecipeRepository界面可能看起来像这样:

@interface RecipeRepository {}
- (void)initWithDelegate:(id)aDelegate;
- (void)findAllRecipes;
- (void)findRecipeById:(NSUInteger)anId;
@end

然后我会为我的代表定义一个协议;现在,这可以作为非正式或正式协议来完成,每种方法的优缺点与此答案无关。我将采用正式的方法:

@protocol RepositoryDelegateProtocol
- (void)repository:(id)repository didRetrieveEntityCollection:(NSArray *)collection;
- (void)repository:(id)repository didRetrieveEntity:(id)entity;
@end

您会注意到我采用了通用方法;您的应用程序中可能会有多个XXXRepository类,并且每个类都将使用相同的协议(您也可以选择提取一个EntityRepository封装了一些通用逻辑的基类)。

现在,要在控制器中使用它,例如,您以前会在其中执行以下操作:

- (void)viewDidLoad 
{
  self.users = [MySingleton getUsers];
  [self.view setNeedsDisplay];
}

你会做这样的事情:

- (void)viewDidLoad 
{
  if(self.repository == nil) { // just some simple lazy loading, we only need one repository instance
    self.repository = [[[RecipeRepository alloc] initWithDelegate:self] autorelease];
  }
  [self.repository findAllRecipes];
}

- (void)repository:(id)repository didRetrieveEntityCollection:(NSArray *)collection;
{
  self.users = collection;
  [self.view setNeedsDisplay];
}

您甚至可以进一步扩展它以使用附加委托方法显示某种“加载”通知:

@protocol RepositoryDelegateProtocol
- (void)repositoryWillLoadEntities:(id)repository;
@end

// in your controller

- (void)repositoryWillLoadEntities:(id)repository;
{
  [self showLoadingView]; // etc.
}

这种设计的另一件事是,您的存储库类实际上不需要是单例的——它们可以在您需要的任何地方实例化。他们可能会处理某种单例连接管理器,但在这一抽象层,单例是不必要的(尽可能避免使用单例总是好的)。

这种方法有一个缺点。您可能会发现在每个级别都需要多层授权。例如,您的存储库可能与执行实际异步数据加载的某种连接对象交互;存储库可能会使用它自己的委托协议与连接对象进行交互。

因此,您可能会发现您必须在应用程序的不同层中“冒泡”这些委托事件,使用的委托越接近您的应用程序级代码,就越粗粒度。这可以创建一个间接层,使您的代码更难遵循。

无论如何,这是我对 SO 的第一个回答,希望对您有所帮助。

于 2009-07-01T23:52:32.953 回答