このライブラリの主な欠点は、通常のドキュメントが 不足していることと、このライブラリ内で何が起こっているかを通常理解するには 不十分であることです。
これは図書館の唯一のマイナスではなく、インターネット上でThree20の長所と短所に関する多くの 議論を見つけることができます。
主な開発者は Three20 の開発を停止し 、新しいNimbusライブラリを作成することに決めたと言わなければなりません。その主なアイデアはシンプルさと高度なドキュメントです。
「NimbusはiOSフレームワークであり、その機能セットはドキュメントと同じ速さでしか成長しません。」
それにもかかわらず、このプロジェクトは今日まで存在し、このプロジェクトのいくつかの部分は注目に値し、デザインの観点からは十分に興味深いものです。
この投稿では、 TTModelの操作に関連する問題について詳しく説明します。
プロジェクトでThree20を使用しない場合は、MVCテンプレートでモデルの注目に値する実装の1つを覗いてみると便利です。
まだ興味のある方にはお願いします 。
TTmodel
MVC(Model-View-Controller)の設計パターンにまだ慣れていない人、またはこれが何であるかを忘れた人は、 Wikiで確認できます。 ( 「繰り返しは学習の母です」 、結局)
建築
そのため、 Three20ライブラリのTTModelはMVCの古典的なモデルです。 つまり、ビューやコントローラーに依存しない抽象的なデータセットにすぎません。
TTModelアーキテクチャは、最初はモデルが異なる状態にある可能性があることを暗示しています。
- -(BOOL)isLoaded-モデルがロードされ、関連データが含まれていることを示すメインフラグ
- -(BOOL)isLoading
- -(BOOL)isLoadingMore-メインデータがロードされたが、追加データの要求が行われたときのモデルの状態
- -(BOOL)isOutdated-モデル内のデータが古いことを示すフラグ。モデルを現在の状態に復元するにはデータを再ロードする必要があります
さらに、TTModelは、それを操作するためのいくつかの基本的なメソッドを提供します。
- -(void)load:(TTURLRequestCachePolicy)cachePolicy more:(BOOL)more-関連するデータをモデルにロードするメソッド
- -(void) cancel-モデルへのデータのロードをキャンセルします
- -(void)invalidate:(BOOL)erase-モデルを「無関係」の状態にする方法。 eraseパラメーターは、キャッシュからデータを削除する必要があることを示します
このアーキテクチャソリューションは非常に正当化されており、モデルの実装を隠すことで問題を解決できます。 実際、最新のデータを取得するためのデータベース内のクエリ、サーバーへのリクエスト、10の連続したクエリの呼び出し、または複雑な計算タスクの実装など、モデルの読み込み方法は重要ではありません。 彼女の状態を知ることは重要です。 そして、状態がisLoadedに変わるとすぐに 、このモデルから実際のデータを取得できます。
// DBRequestModel * someModel = [[DBRequestModel alloc] initWithQuery:@"SELECT * FROM TABLE USERS"]; // [someModel load:TTURLRequestCachePolicyNone more:NO]; // while ([someModel isLoading]) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } if ([someModel isLoaded]) { // result DBRequestModel // NSArray * users = [someModel result]; } else { // ? }
上記のコードは、モデルに関連データがすぐに含まれないことを理解するためだけに書かれています
上記のコードは一部の宇宙に存在する可能性がありますが、私たちのもので使用することはお勧めしません。
モデル状態の変更の追跡
モデルの状態を監視する必要があるのはなぜですか? 実際のデータがいつ存在するかを知るため。
モデルの状態を監視するには、モデルのデリゲート配列にオブジェクト(通常はコントローラー)を追加する必要があります。
[[someModel delegates] addObject:self];
次に、モデルは、状態を変更すると、 TTModelDelegateプロトコルを使用して、すべてのデリゲートにこれを報告します
TTModelDelegateプロトコルは、いくつかの基本的なものを区別する必要がある一連のメソッドを提供します。
- (void)modelDidStartLoad:(id <TTModel>)model-モデルがロードを開始したことを通知します
- (void)modelDidFinishLoad:(id <TTModel>)model-モデルがデータを正常にロードしたことを通知します
- (void)model:(id <TTModel>)model didFailLoadWithError:(NSError *)error-モデルのロードが失敗したときに呼び出されます。 エラーの理由は変数errorに示されています
- (void)modelDidCancelLoad:(id <TTModel>)model-モデルの読み込みがキャンセルメソッドを呼び出してキャンセルされた場合に呼び出されます
全体として、モデルの変更にサブスクライブすることにより、必要なメソッドを実装します。 上記の方法はすべてオプションであり、必要な機能のみに集中できます。
- (void)showModelDataIfPossible { // self.model = [[DBRequestModel alloc] initWithQuery:@"SELECT * FROM TABLE USERS"]; // [[[self model] delegates] addObject:self]; // , // if (! [[self model] isLoaded] && [[self model] isOutdated]) { // [someModel load:TTURLRequestCachePolicyNone more:NO]; } else { // , // [self showModelData:[self model]]; } } #pragma mark - TTModelDelegate - (void)modelDidStartLoad:(id<TTModel>)model { // .. // [self showLoading]; } - (void)modelDidFinishLoad:(id<TTModel>)model { // [self hideLoading]; // // [self showModelData:[self model]]; } - (void)model:(id<TTModel>)model didFailLoadWithError:(NSError*)error { // [self hideLoading]; // User-Friendly TTAlertNoTitle(@"Error code #10"); // User-Friendly [self showReloadScreen]; self.reloadLabel.title = @"Something went wrong" "But you could try to reload data"; } - (void)modelDidCancelLoad:(id<TTModel>)model { // [self hideLoading]; // , // // , // } - (void)showModelData:(id<TTModel>)model { // result DBRequestModel // NSArray * users = [someModel result]; // [self showUsers:users]; }
TTModelとは何か、またTTModelをどのように使用するかについて、多かれ少なかれ明らかになったことを願っています。 次に、モデルのデリゲートについて少し話しましょう。
デリゲートとリスナー
TTModelのデリゲート配列は、オブジェクトが追加されたときにオブジェクトを保持しない特別な配列です。 TTCreateNonRetainingArray関数を使用して作成されます。
これは、循環参照を回避し、 デリゲートを保持して はならないという規則に準拠するために行われます。
実際、プロパティの名前は正しく選択されていません。 機能のデリゲートは、メインオブジェクト(この場合はモデル)が問題を解決するのに役立つはずです。現在の実装では、デリゲートの配列は実際にはリスナーの配列です。
リスナーとデリゲートの主な違いは、ベースオブジェクトの変更についてリスナーに通知されることと、デリゲートがベースオブジェクトの機能に必要なデータを要求されることです。
実際、TTModelでは、機能に基づいたデリゲートの配列はリスナーの配列です。
TTURLRequestModel
ネットワークからデータを取得するモデルを操作するために、Three20はTTURLRequestModelクラスを使用します。 これはTTURLRequestに関連付けられており、モデルの特定の実装を単純に隠すことができるという事実の明確な例を提供します。
TTURLRequestModelは基本的に抽象クラスであり、ネットワークコンポーネントに関連するモデルにいくつかのフィールドを追加しない限り、実際には何も知りません。
このモデルでまだ興味深いのは、通常のドキュメントがないため、どのように機能するのか、どのように正しく使用するのかすぐに明確ではありません:)
独自のモデルを作成しますか?
モデルがいくつかのエンティティのリストを提供する必要があると仮定して、簡単にするためにそれらをユーザーと呼びます。
サーバーはページごとにデータを提供します。 各ページにはN人のユーザーがいます。
途中です!
インターフェースの宣言
@interface ItemsListModel : TTURLRequestModel { /* */ NSMutableArray * _items; /* URL */ NSString * _baseURLString; /* */ int _page; /* */ int _itemsOnPage; } @property(nonatomic, readonly) NSMutableArray * users; @property(nonatomic, readonly) NSString * baseURLString; @property(nonatomic, assign) int page; @property(nonatomic, readonly) int itemsOnPage; /* c URL' */ - (id)initWithBaseURLString:(NSString *)baseURLString itemsOnPage:(int)itemsOnPage; /* , TTURLRequest */ - (TTURLRequest *)requestForDataWithCachePolicy:(TTURLRequestCachePolicy)cachePolicy more:(BOOL) more; /* , NSData * data */ - (NSArray *)parseDataToItems:(NSData *)data error:(NSError **)error; @end
そして実際、私たちはモデルを実装しています
@implementation ItemsListModel @synthesize items = _items; @synthesize itemsOnPage = _itemsOnPage; @synthesize baseURLString = _baseURLString; @synthesize page = _page; /* . . 0 */ - (id)initWithBaseURLString:(NSString *)baseURLString itemsOnPage:(int)itemsOnPage { self = [super init]; if (self) { _baseURLString = [baseURLString copy]; _itemsOnPage = itemsOnPage; _page = 0; _items = [[NSMutableArray array] retain]; _itemsOnPage = 20; } return self; } /* */ - (void)load:(TTURLRequestCachePolicy)cachePolicy more:(BOOL)more { // , , // if ( ! [self isLoading]) { // _isLoadingMore = more; // // if ( ! more) { [self reset]; } // TT_RELEASE_SAFELY(_loadingRequest); _loadingRequest = [[self requestForDataWithCachePolicy:cachePolicy more:more] retain]; // [_loadingRequest send]; } } /* , */ - (TTURLRequest *)requestForDataWithCachePolicy:(TTURLRequestCachePolicy)cachePolicy more:(BOOL) more { // // page // hhtp://baseURL/sdfsd?page=0 // // // NSString * fullRequestURL = self.baseURLString; fullRequestURL = [fullRequestURL stringByAddingQueryDictionary: [NSDictionary dictionaryWithObject: [NSString stringWithFormat:@"%d", _page] forKey:@"page" ] ]; // TTURLRequest * req = [TTURLRequest requestWithURL:fullRequestURL delegate:self]; req.cachePolicy = cachePolicy; req.response = [[TTURLDataResponse new] autorelease]; return req; } /* , */ - (NSArray *)parseDataToItems:(NSData *)data error:(NSError **)error { NSString * dataString = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; // SBJSON NSArray * tweets = [dataString JSONValue]; return tweets; } - (void)requestDidFinishLoad:(TTURLRequest *)request { TTURLDataResponse * response = nil; // , if ([[request response] isKindOfClass:[TTURLDataResponse class]]) { response = (TTURLDataResponse *)request.response; } if ( ! response) { // - [self request:request didFailLoadWithError:nil/* */]; _isLoadingMore = NO; return; } NSError * error = nil; // , NSArray * loadedItems = [self parseDataToItems:[response data] error:&error]; // , if (error) { // ? [self request:request didFailLoadWithError:error]; return; } // , // if ([self isLoadingMore]) { [_items addObjectsFromArray:loadedItems]; } else { TT_RELEASE_SAFELY(_items); _items = [[NSMutableArray arrayWithArray:loadedItems] retain]; } // _page++; if ([loadedItems count] < _itemsOnPage) { /* , , */ // _hasMoreItems = NO } _isLoadingMore = NO; [super requestDidFinishLoad:request]; } /* */ - (void)reset { _page = 0; TT_RELEASE_SAFELY(_items); [super reset]; } /* */ - (void)dealloc { TT_RELEASE_SAFELY(_baseURLString); TT_RELEASE_SAFELY(_items); [super dealloc]; } @end
最後に、実際の条件でモデルを使用する例
@implementation MyModelViewController #pragma mark - View LifeCycle - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self createAndStartModelIfNeeded]; } //... #pragma mark - Model Showing - (void)showModelData:(id<TTModel>)model { // result ItemsListModel // NSArray * tweets = [(ItemsListModel *)model items]; // " " NSLog(@"tweets : %{} ", tweets); // :( // @"tweets : %@" // - status Label if ([tweets count]) { NSDictionary * firstTweet = [tweets objectAtIndex:0]; NSString * tweetText = [firstTweet objectForKey:@"text"]; _statusLabel.text = [[_statusLabel text] stringByAppendingFormat:@"\n%@", tweetText]; } } #pragma mark - Model Creationg - (void)createAndStartModelIfNeeded { // if ( ! _model) { _model = [[ItemsListModel alloc] initWithBaseURLString:@"http://api.twitter.com/1/statuses/user_timeline.json?user_id=18191307" itemsOnPage:20]; [[_model delegates] addObject:self]; } // - if ( ! [_model isLoaded] ) { [_model load:TTURLRequestCachePolicyNone more:NO]; } } #pragma mark - TTModel Delegate - (void)modelDidStartLoad:(id<TTModel>)model { _statusLabel.text = @""; } - (void)modelDidFinishLoad:(id<TTModel>)model { _statusLabel.text = @"! "; [self showModelData:model]; } - (void)model:(id<TTModel>)model didFailLoadWithError:(NSError*)error { _statusLabel.text = [NSString stringWithFormat:@":(( %@", [error localizedDescription]]; } - (void)modelDidCancelLoad:(id<TTModel>)model { _statusLabel.text = @" "; } @end
気にする人は、ソースはここからダウンロードできます 。 Three20接続でプロジェクトを作成する喜びからHabrユーザーを救おうとはしなかったので、アーカイブにはモデルとViewControllerを持つファイルのみがあります。
まとめ
Three20ライブラリのTTModelは、iOS用のプログラミング時にMVCアプローチを整理するために使用できる非常に優れた抽象化です。 Three20を使用しない場合でも、TTModelの実装を確認し、必要に応じて提示されたモデルに基づいてモデルを作成することは価値があります。