8

これが私の問題です:

  • コア データ (速度と接続の問題) を使用して、IOS アプリを構築したいと考えています。コア データに格納されているデータは、まだ定義されていない Web サービスを介してアクセスできる SQLServer データベースから取得されます。
  • コア データに格納されているデータへの変更は、Web サービスを介して SQLServer と同期する必要があります。さらに、接続の問題が原因で同期されない変更をバッファリングする必要があります。
  • また、サーバーで発生したすべての変更でコア データを更新する必要があります。これは、ユーザー設定で設定されたスケジュールで発生する可能性があります。

私が調査したソリューション:

  • NSIncrementalStoreクラスの使用(IOS 5 の新機能)。これが正確に何をするのか非常に混乱していますが、有望に思えます。NSIncrementalStore私が知る限り、通常のコア データ API 呼び出しをインターセプトできるようにするサブクラスです。次に、その情報をコア データに渡し、Web サービスを介して外部データベースと同期することができました。私は完全に間違っている可能性があります。しかし、私が正しいと仮定すると、インターネットへの接続がダウンしている場合、どのようにデルタを同期するのでしょうか?
  • AFIncrementalStore- これは、Web サービス部分を実行するためにNSIncrementalStore使用するサブクラスです。AFNetworking
  • RestKit- この API がどれほどアクティブであるかが少し気になります。ブロック機能への移行が進んでいるようです。誰かがこれを広範囲に使用しましたか?

AFIncrementalStoreこれは(と思われる)より標準的なアプローチを使用しているため、私は傾いています。問題は、私がNSIncrementalStore本当のことを完全に理解していない可能性があることです。

サンプルコードやチュートリアルへのリンクは素晴らしいでしょう!

4

2 に答える 2

5

これに対する私の解決策は、データ セットの 2 つのコピーを CoreData データベースに格納することでした。1 つは最後の既知のサーバー状態を表し、不変です。もう 1 つはユーザーが編集します。

変更を同期するとき、アプリはデータの編集済みコピーと不変コピーの間の差分を作成します。アプリは、差分をデータの独自のコピーに適用する Web サービスに差分を送信します。アプリはデータセットの完全なコピーで応答し、アプリはそれをデータの両方のコピーに上書きします。

利点は次のとおりです。

  • ネットワーク接続がない場合、変更は失われません。データ セットを送信する必要があるたびに差分が計算され、不変のコピーは同期が成功した場合にのみ変更されます。
  • 送信する必要がある最小限の情報のみが送信されます。
  • 上書きによるデータ損失の可能性を最小限に抑えながら、ロック戦略を使用せずに複数のユーザーが同じデータを同時に編集できます。

欠点は次のとおりです。

  • 差分コードの記述は複雑です。
  • マージ サービスの記述は複雑です。
  • あなたがメタプログラミングの第一人者でない限り、差分/マージ コードは脆く、オブジェクト モデルを変更するたびに変更しなければならないことに気付くでしょう。

戦略を考え出す際に私が考えたいくつかの考慮事項を次に示します。

  • 変更をオフラインで行うことを許可すると、チェックイン/チェックアウトのロックは機能しません (接続なしでロックを確立するにはどうすればよいでしょうか?)。
  • 2 人が同時に同じデータを編集するとどうなりますか?
  • ある人がコネクションレス状態の iOS デバイスでデータを編集し、それをオフにして別のデバイスで編集し、元のデバイスを再びオンにするとどうなりますか?
  • CoreData を使用したマルチスレッドは、それ自体が完全な問題クラスです。

このようなリモートで何かを行うためのすぐに使えるサポートに最も近いと聞いたのは、iOS6 の新しい iCloud/CoreData 同期システムです。エンティティが変更されると、CoreData データベースから iCloud にエンティティを自動的に送信します。ただし、iCloud を使用する必要があります。

編集:これは非常に遅いですが、2つの NSManagedObject インスタンス間の差分を生成できるクラスを次に示します。

// SZManagedObjectDiff.h
@interface SZManagedObjectDiff

- (NSDictionary *)diffNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject

@end

// SZManagedObjectDiff.m
#import "SZManagedObjectDiff.h"

@implementation SZManagedObjectDiff

- (NSDictionary *)diffNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {

    NSDictionary *attributeDiff = [self diffAttributesOfNewObject:newObject withOldObject:oldObject];

    NSDictionary *relationshipsDiff = [self diffRelationshipsOfNewObject:newObject withOldObject:oldObject];

    NSMutableDictionary *diff = [NSMutableDictionary dictionary];

    if (attributeDiff.count > 0) {
        diff[@"attributes"] = attributeDiff;
    }

    if (relationshipsDiff.count > 0) {
        diff[@"relationships"] = relationshipsDiff;
    }

    if (diff.count > 0) {
        diff[@"entityName"] = newObject ? newObject.entity.name : oldObject.entity.name;

        NSString *idAttributeName = newObject ? newObject.entity.userInfo[@"id"] : oldObject.entity.userInfo[@"id"];

        if (idAttributeName) {
            id itemId = newObject ? [newObject valueForKey:idAttributeName] : [oldObject valueForKey:idAttributeName];

            if (itemId) {
                diff[idAttributeName] = itemId;
            }
        }
    }

    return diff;
}

- (NSDictionary *)diffRelationshipsOfNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {

    NSMutableDictionary *diff = [NSMutableDictionary dictionary];

    NSDictionary *relationships = newObject == nil ? [[oldObject entity] relationshipsByName] : [[newObject entity] relationshipsByName];

    for (NSString *name in relationships) {

        NSRelationshipDescription *relationship = relationships[name];

        if (relationship.deleteRule != NSCascadeDeleteRule) continue;

        SEL selector = NSSelectorFromString(name);

        id newValue = nil;
        id oldValue = nil;

        if (newObject != nil && [newObject respondsToSelector:selector]) newValue = [newObject performSelector:selector];
        if (oldObject != nil && [oldObject respondsToSelector:selector]) oldValue = [oldObject performSelector:selector];

        if (relationship.isToMany) {

            NSArray *changes = [self diffNewSet:newValue withOldSet:oldValue];

            if (changes.count > 0) {
                diff[name] = changes;
            }

        } else {

            NSDictionary *relationshipDiff = [self diffNewObject:newValue withOldObject:oldValue];

            if (relationshipDiff.count > 0) {
                diff[name] = relationshipDiff;
            }
        }
    }

    return diff;
}

- (NSDictionary *)diffAttributesOfNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {

    NSMutableDictionary *diff = [NSMutableDictionary dictionary];

    NSArray *attributeNames = newObject == nil ? [[[oldObject entity] attributesByName] allKeys] : [[[newObject entity] attributesByName] allKeys];

    for (NSString *name in attributeNames) {

        SEL selector = NSSelectorFromString(name);

        id newValue = nil;
        id oldValue = nil;

        if (newObject != nil && [newObject respondsToSelector:selector]) newValue = [newObject performSelector:selector];
        if (oldObject != nil && [oldObject respondsToSelector:selector]) oldValue = [oldObject performSelector:selector];

        newValue = newValue ? newValue : [NSNull null];
        oldValue = oldValue ? oldValue : [NSNull null];

        if (![newValue isEqual:oldValue]) {
            diff[name] = @{ @"new": newValue, @"old": oldValue };
        }
    }

    return diff;
}

- (NSArray *)diffNewSet:(NSSet *)newSet withOldSet:(NSSet *)oldSet {

    NSMutableArray *changes = [NSMutableArray array];

    // Find all items that have been newly created or updated.
    for (NSManagedObject *newItem in newSet) {

        NSString *idAttributeName = newItem.entity.userInfo[@"id"];

        NSAssert(idAttributeName, @"Entities must have an id property set in their user info.");

        id newItemId = [newItem valueForKey:idAttributeName];

        NSManagedObject *oldItem = nil;

        for (NSManagedObject *setItem in oldSet) {
            id setItemId = [setItem valueForKey:idAttributeName];

            if ([setItemId isEqual:newItemId]) {
                oldItem = setItem;
                break;
            }
        }

        NSDictionary *diff = [self diffNewObject:newItem withOldObject:oldItem];

        if (diff.count > 0) {
            [changes addObject:diff];
        }
    }

    // Find all items that have been deleted.
    for (NSManagedObject *oldItem in oldSet) {

        NSString *idAttributeName = oldItem.entity.userInfo[@"id"];

        NSAssert(idAttributeName, @"Entities must have an id property set in their user info.");

        id oldItemId = [oldItem valueForKey:idAttributeName];

        NSManagedObject *newItem = nil;

        for (NSManagedObject *setItem in newSet) {
            id setItemId = [setItem valueForKey:idAttributeName];

            if ([setItemId isEqual:oldItemId]) {
                newItem = setItem;
                break;
            }
        }

        if (!newItem) {
            NSDictionary *diff = [self diffNewObject:newItem withOldObject:oldItem];

            if (diff.count > 0) {
                [changes addObject:diff];
            }
        }
    }

    return changes;
}

@end

それが何をするか、どのように行うか、およびその制限/仮定についての詳細は次のとおりです。

http://simianzombie.com/?p=2379

于 2012-08-14T22:01:18.113 に答える