我對此的解決方案是將數據集的兩個副本存儲在CoreData數據庫中。一個代表最後一個已知的服務器狀態,並且是不可變的。另一個是由用戶編輯的。
當需要同步更改時,應用程序會在已編輯和不可變的數據副本之間創建差異。應用程序將diff發送到一個Web服務,該服務將diff應用於其自己的數據副本。它用數據集的完整副本進行回覆,該應用程序將覆蓋該數據的兩個副本。
的優點是:
- 如果沒有網絡連接,沒有任何更改將丟失:中DIFF在每個數據集需要被髮送的時間計算,並且不可改變的副本只改變了一個成功的同步。
- 只發送需要發送的最小信息量。
- 多人可以同時編輯相同的數據,而無需使用鎖定策略,並通過覆蓋來減少數據丟失的機會。
缺點是:
- 編寫代碼版本比較複雜。
- 編寫合併服務很複雜。
- 除非您是元編程專家,否則您會發現您的差異/合併代碼很脆弱,並且每當您更改對象模型時都必須更改。
這裏有一些上來時與策略,我有考慮的:
- 如果允許更改離線製作,簽入/籤鎖定將無法正常工作(你怎麼能建立一個鎖定沒有連接?)。
- 如果兩個人同時編輯相同的數據會發生什麼?
- 如果一個人在無連接時在一臺iOS設備上編輯數據,將其關閉,在另一臺設備上編輯然後重新打開原始設備,會發生什麼情況?
- CoreData多線程本身就是一個完整的問題類。
我聽說到外的現成支持做任何遠程這樣最接近的是iOS6的,它會自動從數據庫CoreData到iCloud傳送實體新的iCloud/CoreData同步系統當他們改變時。但是,這意味着您必須使用iCloud。
編輯:這是非常晚,我知道,但是這裏有一個類能夠產生兩個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-14 22:01:18
Ant
我認爲你正在討論的同步功能可以在IOS5中找到,但是show-stopper(正如你所引用的)是它與iCloud同步,而不是我必須與之交互的系統(SQLServer)。我很喜歡你的想法,但我很關心你指出的問題:找出三角洲。你有使用NSIncrementalStore的經驗嗎?我仍然對它究竟是什麼感到困惑。 – JustLearningAgain 2012-08-15 02:23:40
NSIncrementalStore是用於將任何類型的存儲系統實現爲CoreData存儲的基類。想用CoreData API使用XML文件嗎?從NSIncrementalStore繼承並編寫方法來執行此操作。我認爲這不會對你的情況有所幫助。 – Ant 2012-08-15 03:21:05
它允許我做到兩個嗎?我可以用它來更新核心數據以及我的外部Web服務嗎? – JustLearningAgain 2012-08-15 07:11:39