Web から解析されたデータを提供するモデル クラスを開発しています。もちろん、アプリケーションをレスポンシブにしたいので、ネットワーキングは別のスレッド/キューで行う必要があります。
これにより、クラスの @interface をどのように設計すればよいでしょうか?という疑問が生じます。
主な要件は次のとおりです。
- モデルからビューコントローラーにデータを配信する必要があります:);
- メイン (UI) スレッドをブロックするべきではありません。
- 理解しやすく、他の開発者がフォローしやすいものにする必要があります。
WWDC2012 のビデオ「Building Concurrent User Interfaces on iOS」から学んだことから、Apple はコンカレント コードをモデルを使用するクラス自体に移動することを推奨しています。
ViewController に NSArray* 形式の最新の投稿を提供する Posts クラス (Posts.h/.m) があるとします。
オプション 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 -- 完了ハンドラー パターンによる並行性
現在アプリで使用している 2 番目のオプションは、完了ハンドラー パターンです。完了ハンドラーは、長いネットワークが実行された後にのみ呼び出されるため、メイン スレッドをブロックしません。
//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 はかなり複雑で、メソッド名は長く、(私の観点からは)難読化されています。
オプション III -- 委任パターン
私が目にするもう 1 つのオプションは、デリゲート パターンです。気になるのは、ViewController が 1 つしかデリゲートにならない可能性があることです。そのため、すべての VC を 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
だからここに質問があります:
- Model から Controller にノンブロッキングでデータを配信するための要件に適合するパターンは他にあるでしょうか?
- あなたの経験、実践、または理論的知識に基づいて、どのオプションをお勧めしますか?