これに対する私の解決策は、データ セットの 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