2014-10-30 61 views
1

我正在同步客戶端和服務器之間的數據。我正在使用MagicalRecord(一種核心數據包裝器)將數據存儲在客戶端上。我有一個名爲Dirty的實體,其中包含一個名爲dirty的屬性。這表示客戶端上是否有尚未推送到服務器的更改。 dirty設置爲[NSDate date]任何時候在課程上設置了一個屬性(當然,在設置dirty時,設置了正確的值)。在客戶端上創建的每個其他實體都從Dirty繼承。這個想法是,我們不會從服務器獲取新的數據,直到所有的客戶端數據都被推上去(如果所有的實體都有dirty == nil,那麼只能獲取新的數據)。在覈心數據上使用髒標誌與服務器同步

從服務器導入數據時(使用+[NSManagedObject MR_importFromObject:inContext:),每個實體的dirty屬性被設置爲nil(因爲客戶端與服務器是最新的)。

在保存啓動之前(在+[MagicalRecord saveWithBlock:completion:]保存塊內),dirty仍然是nil。但是,在完成塊中,獲取剛剛保存的實體(在主線程上)的值爲dirty

保存期間,實體被轉移到主線程的上下文。但是,有一個問題,因爲-[NSManagedObject didChangeValueForKey:]被調用,從localContext(後臺線程)傳輸到主上下文(在主線程上)的每個屬性。 dirty爲每個實體設置爲[NSDate date]。大多數情況下,dirty不會被最後設置,這意味着當另一個屬性被設置時,dirty被覆蓋。

有沒有辦法確保dirty是將NSManagedObject實例轉移到主線程上下文時設置的最後一個屬性?當對象被保存時(而不是當屬性被設置時),我甚至願意設置dirty

我試過各種選項,包括檢查-[NSManagedObject isInserted]-[NSManagedObject isUpdated]裏面-[NSManagedObject didChangeValueForKey:]。另一件令人討厭的事情是,在傳輸屬性之前插入新對象(我想我可以有某種標誌來鎖定/解鎖設置dirty)。

要注意的另一件事是[NSManagedObject(_NSInternalMethods) _updateFromRefreshSnapshot:includingTransients:]是在新對象上調用-[NSManagedObject didChangeValueForKey:]之前被調用的對象。

任何想法?我已經在這幾天面對這個問題了。

回答

0

在浪費了大量時間在潛在解決方案之後,我回過頭來看看以最簡單的方式解決這個問題。以下是我想出了:

1)刪除創建一個BTCoreDataService-[Dirty didChangeValueForKey:]

2),並增加了以下方法:

+ (void)saveClientChangesWithSaveBlock:(BTLocalManagedObjectContextBlock)saveBlock 
         completionBlock:(MRSaveCompletionHandler)completionBlock { 
    [self saveAndSetDirty:[NSDate date] 
       saveBlock:saveBlock 
      completionBlock:completionBlock]; 
} 

+ (void)saveServerChangesWithSaveBlock:(BTLocalManagedObjectContextBlock)saveBlock 
         completionBlock:(MRSaveCompletionHandler)completionBlock { 
    [self saveAndSetDirty:nil 
       saveBlock:saveBlock 
      completionBlock:completionBlock]; 
} 

#pragma mark - Internal 

+ (void)saveAndSetDirty:(NSDate *)dirty 
       saveBlock:(BTLocalManagedObjectContextBlock)saveBlock 
     completionBlock:(MRSaveCompletionHandler)completionBlock { 
    [MagicalRecord 
    saveWithBlock:^(NSManagedObjectContext *localContext) { 
     if (saveBlock) { 
      saveBlock(localContext); 

      [[localContext BT_insertedAndUpdatedAtObjectsKindOfClass:[Dirty class]] 
       makeObjectsPerformSelector:@selector(setDirty:) withObject:dirty]; 
     } 
    } 
    completion:completionBlock]; 
} 

這裏是NSManagedObjectContext+BTManagedObjectContext實現:

- (NSSet *)BT_insertedObjectsKindOfClass:(Class)cls { 
    return 
    [self.insertedObjects 
    filteredSetUsingPredicate:[self BT_isKindOfClassPrediate:cls]]; 
} 

- (NSSet *)BT_updatedObjectsKindOfClass:(Class)cls { 
    return 
    [self.updatedObjects 
    filteredSetUsingPredicate:[self BT_isKindOfClassPrediate:cls]]; 
} 

- (NSSet *)BT_insertedAndUpdatedAtObjectsKindOfClass:(Class)cls { 
    return 
    [[self BT_insertedObjectsKindOfClass:cls] 
    setByAddingObjectsFromSet:[self BT_updatedObjectsKindOfClass:cls]]; 
} 

#pragma mark - Internal 

- (NSPredicate *)BT_isKindOfClassPrediate:(Class)cls { 
    return [NSPredicate predicateWithFormat:@"self isKindOfClass:%@", cls]; 
} 

現在唯一要記住使用BTCoreDataService來保存對象而不是使用01直接。

1

已經隨着10723861

的「TrackedEntity」看看從保羅·德·蘭格的答案在那裏會成爲你Dirty實體和屬性lastModified會轉化爲你的屬性「髒」

在一保存(通過觀察NSManagedObjectContextWillSaveNotification觸發),-objectContextWillSave方法將把插入的對象和更新的對象合併到一個集合中。然後,它會遍歷該對象集合並使用時間戳更新lastModified屬性。

--- Update(wrt。clientUpdatedAt

您可能也想看看at this one。它解釋瞭如何使用一些額外的字段來協助同步。使用額外的屬性sync_status應該有助於識別是否需要上傳實體。希望有幫助

+0

所以我已經用'clientUpdatedAt'(和'clientCreatedAt')來做這件事。 'clientUpdatedAt'不是一個好的指示,因爲當服務器更新客戶端上的實體時,服務器的'updatedAt'time,實體必須保存並且'clientUpdatedAt'再次用'+ [NSDate date]'更新。如果我檢查'clientUpdatedAt'是否比'updatedAt'更新,它會每次返回'YES'。 :/ – Jeff 2014-10-31 12:21:53

+0

我已經更新了我的答案,您可能需要引入更多屬性來處理同步 – Olaf 2014-10-31 13:17:00

+0

感謝您更新您的答案。我們面臨的挑戰是:在模型對象上將「sync_status設置爲1」,只要發生了某些變化並需要與服務器同步即可。這是我在代碼中遇到的同樣的挑戰。我嘗試使用'didChangeValueForKey:',但由於我在原始文章中提到的原因,這是不可靠的。你對如何完成這一步有什麼建議嗎?一旦確定了這一點,其他一切都將順利進行。 – Jeff 2014-10-31 13:33:08