gpt4 book ai didi

ios - 具有后台网络的模型的首选设计模式

转载 作者:行者123 更新时间:2023-11-28 22:25:12 25 4
gpt4 key购买 nike

我正在开发一个模型类,它提供从网络解析的数据。当然,我希望我的应用程序能够响应,因此网络应该在单独的线程/队列上完成。

这引出了一个问题:我应该如何设计类的@interface

主要要求是:

  • 它应该将数据从 Model 传递到 Viewcontroller :) ;
  • 它不应阻塞主 (UI) 线程;
  • 它应该易于其他开发者理解和遵循。

根据我从 WWDC2012 视频“在 iOS 上构建并发用户界面”中学到的知识,Apple 建议将并发代码移动到使用模型的类本身。

假设我们有 Posts 类 (Posts.h/.m),它应该为 ViewController 提供 NSArray* 格式的最新帖子。

选项 I -- 并发在用户类中

类本身不是并发的,但是用户是:

//Posts.h:
@interface Posts : NSObject

- (NSArray*)getPostsForUser:(NSString*)user;

@end


//ViewController.m
@implementation ViewController

- (void)someFunctionThatUpdatesUI
{
//<...>
NSOperationQueue *q = [[NSOperationQueue alloc] init];
[q addOperationWithBlock:^{
NSArray *currentPosts = [Posts shared] getPostsForUser:@"mike";
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//UI should be updated only on main thread
[self updateUIWithPosts:currentPosts];
}];
}];
//<...>
}

这种方法的主要缺点是必须在每个 ViewController 中重复几乎相同的代码。如果有几十个怎么办?

选项 II -- 与完成处理程序模式的并发

我目前在我的应用程序中使用的第二个选项是完成处理程序模式。由于完成处理程序仅在执行了一些长时间的网络后被调用,它不会阻塞主线程:

//Posts.h:
@interface Posts : NSObject

- (NSError*)getPostsForUser:(NSString*)user
withCompletionHandler:(void(^)(NSArray*))handler;

@end

@implementation Posts

- (NSError*)getPostsForUser:(NSString*)user
withCompletionHandler:(void(^)(NSArray*))handler
{
//<...>
dispatch_async(dipatch_get_global_queue(0, 0), ^{
//Do some may-be-long networking stuff here,
//parse, etc and put it into NSArray *result
dispatch_async(dipatch_get_main_queue(), ^{
handler(result);
});
});
//<...>
}

//ViewController.m
- (void)someFunctionThatUpdatesUI
{
//<...>
[Posts shared] getPostsForUser:@"mike"
withCompletionHandler:^(NSArray* posts){
//updateUI with posts
}];
}

从我的角度来看,这种方式很好,但是@interface 相当复杂,方法名称很长并且(从我的角度来看)混淆。

方案三——委托(delegate)模式

我看到的另一个选项是委托(delegate)模式。困扰我的是,只有一个 ViewController 可能是委托(delegate),因此需要将每个 VC 设置为委托(delegate) - viewWillAppear,这很容易忘记。

//Posts.h:
@protocol PostUpdate<NSObject>

- (void)updateUIWithPosts:(NSArray*)posts FromUser:(NSString*)user;

@end

@interface Posts

- (NSError*)updatePostsFromUser:(NSString*)user;
@property(nonatomic, weak) id<PostUpdate> delegate;

@end

//ViewController.m:
@implementation ViewController<PostUpdate>

- (void)viewWillAppear
{
//<...>
[Posts shared].delegate = self;
}

- (IBAction)getLatestsPostButtonPressed:(id)sender
{
[[Posts shared] updatePostsFromUser:@"mike"];
}

// protocol PostUpdate
- (void)updateUIWithPosts:(NSArray*)posts FromUser:(NSString*)user
{
//updating UI with posts
}

@end

下面是问题:

  • 还有哪些模式符合以非阻塞方式将数据从模型传递到 Controller 的要求?
  • 根据您的经验、实践或理论知识,您会推荐什么选项?

最佳答案

简答

我建议使用选项 II,因为它是最适合您情况的解决方案。

长答案

首先,这三种解决方案都没有错,但我们只是想找出最适合您情况的解决方案。

• 选项-I 的问题在于它始终是同步 并且会在您需要异步 时阻塞调用线程,因此,您会发现你自己总是从后台线程调用它,这意味着会有很多重复的代码,这会使维护变得更加困难(如果你需要该方法有时是异步的,而大部分时间是同步的,则此选项可能是一个很好的解决方案)。

• 选项-II 通过提供一种在数据准备就绪时通知调用者线程的方法解决了这个问题,并且与它提供的易用性和灵 active 相比,添加的参数并不是真正的缺点。如果您认为在某些情况下您真的不需要添加的参数,您可以简单地制作另一个不带该参数的方法的同步版本:

- (NSArray *)fetchPostsForUser:(NSString*)user; /// Synchronous
- (void)fetchPostsForUser:(NSString*)user /// Asynchronous
completion:(CompletionHandler)completion;

CompletionHandler 定义如下:

typedef void (^CompletionHandler)(NSArray *result, NSError *error);

• 第三个选项并不是解决您问题的真正好方法。委托(delegate)应该用于传递有关类本身的事件,而不是传递对先前调用的方法的响应。另外,请注意你只能有一个委托(delegate),这意味着你不能同时从两个不同的 Controller 调用这种方法。

关于ios - 具有后台网络的模型的首选设计模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19201513/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com