今是昨非

今是昨非

日出江花红胜火,春来江水绿如蓝

MagicalRecordの使用

データベースの作成#

1. まず自分が何を必要としているかを分析する#

私の目的は、手紙のリスト画面をキャッシュすることです。新しいリスト画面のモデルは letter なので、Letter のエンティティが必要です。では、この Letter にはどんな属性があるのでしょうか?書き手 (sender)、手紙の内容 (content)、手紙の時間 (dateString)、手紙の既読未読状態 (isRead)、送信か受信か (incoming);なので、私の Letter のエンティティを作成した後はこのようになります:

image1

これで十分でしょうか?いいえ、普通の表示画面であれば、letter のエンティティだけで十分ですが、私のこのプロジェクトは手紙を表示するもので、比較的プライベートなものです。私がキャッシュした手紙のリストは私だけが見るべきです。他の人が私の携帯電話にログインしたらどうなるでしょう?区別をしていないため、彼がログインすると、私のデータが見えてしまいます。データは携帯電話にキャッシュされているからです!

したがって、User のエンティティが必要です。この User のエンティティの目的は:Letter と結びつけて、各自が自分が見るべきものを見られるようにすることです;user には 2 つの属性があります:account と writeName(通常は uid ですが、私のは比較的簡単なので uid はありません);

image2

次の問題は、Letter と User の間の関係です。一対一か一対多か、私の各 letter には一つの user が必要で、そして一つの user には多くの letter が必要です。データベースから letter を取得するとき、実際には user を使って検索しています。user と letter が一対一の場合、私は一通の手紙しか取得できませんが、それは明らかに間違っています。
image3

image4

結論:私のデータベースには 2 つのエンティティがあります。一つは Letter、一つは User;Letter と User の関係のタイプは一対一で、User と Letter の関係のタイプは一対多です;

2. 次にMagicalRecordをインポートする#

私は以前、データをキャッシュする際に FMDB を使用しており、CoreData は使ったことがありませんでしたが、MVC に完全に分割する際に、私はこのモデルを使って直接保存し、取り出すとモデルとして直接使用できることを望んでいました。属性を一つ一つ再設定するのではなく、CoreData を試してみたいと思いました。しかし、原生のものは複雑すぎるので、MagicalRecord を選びました。

MagicalRecord の使用:github には各メソッドの使用法しかありませんが、デモを見つけられなかったので、私がどのように使用したかを貼り付けます。参考までに。

pch ファイルにヘッダーファイルをインポートします。
a. applicationDidFinishLaunchingWithOptions: で初期化します。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // アプリケーション起動後のカスタマイズのためのオーバーライドポイント。
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    
    [MagicalRecord setupAutoMigratingCoreDataStack];    
    // windowのrootViewControllerを設定
    [self setupWindowRootViewController];

    return YES;
}

b. アプリケーションが停止する際に、cleanup メソッドを呼び出します。

- (void)applicationWillTerminate:(UIApplication *)application {
    // アプリケーションが終了しようとしています。適切な場合はデータを保存します。applicationDidEnterBackground:も参照してください。
    [MagicalRecord cleanUp];
}

c. モデルクラスを生成します。
.xcdatamodeld ファイルを選択し、CMD+N を押します。

image5

すべて選択して、モデルクラスを生成します。

d. ネットワークリクエストが成功した後、データを保存します。

// letterをデータベースに保存
- (void)saveLetterWithLetterEntity:(LetterEntity *)tempLetter {
   // MagicalRecordの保存メソッド、メインスレッドではない
    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
      // まず、pkNumberで検索してletterを取得
      // letterが存在しない場合は作成
      // 値を設定して保存、ここでのuser.lettersはNSSetであることに注意
        Letter *letter = [Letter MR_findFirstByAttribute:@"pkNumber" withValue:tempLetter.pkNumber inContext:localContext];
        if (!letter) {
           // letterを作成
            letter = [Letter MR_createEntityInContext:localContext];
            User *user = [User MR_createEntityInContext:localContext];
            letter.user = user;
        }
        letter.dateString = tempLetter.letterDateString;
        letter.content = tempLetter.letterContent;
        letter.sender = tempLetter.letterSender;
        letter.pkNumber = tempLetter.pkNumber;
        letter.incoming = [NSNumber numberWithBool:tempLetter.incoming];
        // 受信はデフォルトで未読、送信はデフォルトで既読
        if (tempLetter.incoming) {
            // 受信
            letter.isRead = [NSNumber numberWithBool:NO];
        }
        else {
            letter.isRead = [NSNumber numberWithBool:YES];
        }
        
        letter.user.writeName = [[NSUserDefaults standardUserDefaults] objectForKey:k_WRITENAME];
        letter.user.account = [[NSUserDefaults standardUserDefaults] objectForKey:k_USERNAME];
        if (_letters == nil) {
            _letters = [NSMutableArray array];
        }
        [_letters addObject:letter];
        letter.user.letters = [NSSet setWithArray:_letters];
    } completion:^(BOOL contextDidSave, NSError *error) {
        DLog(@"=-===%@", (contextDidSave ? @"saveSuccessed" : @"saveFailure"));
    }];
}

e. ネットワークが失敗した場合、データベースからデータを取得します。

- (NSMutableArray *)lettersFromDataBase {

    NSMutableArray *receiveArray = [NSMutableArray array];
    NSMutableArray *sendArray = [NSMutableArray array];
    
    NSString *account = [[NSUserDefaults standardUserDefaults] objectForKey:k_USERNAME];
    User *user = [[User MR_findByAttribute:@"account" withValue:account] firstObject];

//    NSPredicate *receivePredicate = [NSPredicate predicateWithFormat:@"incoming == %@ && user == %@", [NSNumber numberWithBool:YES], user];
//    NSPredicate *sendPredicate = [NSPredicate predicateWithFormat:@"incoming == %@ && user == %@", [NSNumber numberWithBool:NO], user];
//    receiveArray = [NSMutableArray arrayWithArray:[Letter MR_findAllWithPredicate:receivePredicate]];
//    sendArray = [NSMutableArray arrayWithArray:[Letter MR_findAllWithPredicate:sendPredicate]];

    
    NSArray *userLetters = [Letter MR_findByAttribute:@"user" withValue:user];
    if (userLetters) {
        for (int i = 0; i < userLetters.count; i++) {
            Letter *tempLetter = userLetters[i];
            LetterEntity *tempEntity = [[LetterEntity alloc] init];
            tempEntity.letterContent = tempLetter.content;
            tempEntity.letterDateString = tempLetter.dateString;
            tempEntity.letterSender = tempLetter.sender;
            tempEntity.pkNumber = tempLetter.pkNumber;
            tempEntity.incoming = [tempLetter.incoming boolValue];

            if ([tempLetter.incoming boolValue]) {
                [receiveArray addObject:tempEntity];
            }
            else {
                [sendArray addObject:tempEntity];
            }
        }
    }
    // ここでの順序は間違ってはいけません。sendArrayが前、receiveArrayが後です。
    NSMutableArray *resultArray = [NSMutableArray arrayWithObjects: sendArray, receiveArray, nil];
    return resultArray;
}

参考#

  1. 深入浅出 MagicalRecord、このブログは非常に詳細で、CoreData から MagicalRecord まで説明しています。

  2. RayWenderlich の MagicalRecord Tutorial、このチュートリアルは、実際に打ち込んで練習できます。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。