2011-02-02 3 views
21

속성에 modificationDate이 있습니다. Entity A.NSManagedObject이 저장 될 때마다 해당 값을 설정하고 싶습니다. 그래서코어 데이터 willSave : 방법

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Failed to process pending changes before save. The context is still dirty after 100 attempts. Typically this recursive dirtying is caused by a bad validation method, -willSave, or notification handler.' *** 

, 내가 궁금하네요, modificationDate의 값을 설정하는 가장 좋은 방법은 무엇입니까 : 내가 NSManagedObjectwillSave: 방법으로 그렇게하려고 경우, 오류가 발생합니다? willSave의 NSManagedObject 워드 프로세서에서

답변

32

:의 라인을 따라

If you want to update a persistent property value, you should typically test for equality of any new value with the existing value before making a change. If you change property values using standard accessor methods, Core Data will observe the resultant change notification and so invoke willSave again before saving the object’s managed object context. If you continue to modify a value in willSave, willSave will continue to be called until your program crashes.

For example, if you set a last-modified timestamp, you should check whether either you previously set it in the same save operation, or that the existing timestamp is not less than a small delta from the current time. Typically it’s better to calculate the timestamp once for all the objects being saved (for example, in response to an NSManagedObjectContextWillSaveNotification).

그래서 아마 뭔가 : 당신은 1.0를 조정할 수 있습니다

-(void)willSave { 
    NSDate *now = [NSDate date]; 
    if (self.modificationDate == nil || [now timeIntervalSinceDate:self.modificationDate] > 1.0) { 
     self.modificationDate = now; 
    } 
} 

요청 저장 예상하여 사이의 최소 델타를 반영합니다.

+0

을 설정 할 수 있습니다 관찰을 사용하여 필요하지 않습니다 changedValues ​​()에 나타납니다, 설정 edValues ​​ – malhal

47

실제로는 apple docs (허용되는 대답의 절반 만 읽음)은이 방법을 권장하지 않습니다. 그들은 명시 적으로 NSManagedObjectContextWillSaveNotification을 사용해야한다고 말합니다. 예는 다음과 같을 수 있습니다 대부분의 핵심 데이터 프로젝트의 추상 기본 클래스로 :

@interface TrackedEntity : NSManagedObject 
@property (nonatomic, retain) NSDate* lastModified; 
@end 

@implementation TrackedEntity 
@dynamic lastModified; 

+ (void) load { 
    @autoreleasepool { 
     [[NSNotificationCenter defaultCenter] addObserver: (id)[self class] 
               selector: @selector(objectContextWillSave:) 
                name: NSManagedObjectContextWillSaveNotification 
                object: nil]; 
    } 
} 

+ (void) objectContextWillSave: (NSNotification*) notification { 
    NSManagedObjectContext* context = [notification object]; 
    NSSet* allModified = [context.insertedObjects setByAddingObjectsFromSet: context.updatedObjects]; 
    NSPredicate* predicate = [NSPredicate predicateWithFormat: @"self isKindOfClass: %@", [self class]]; 
    NSSet* modifiable = [allModified filteredSetUsingPredicate: predicate]; 
    [modifiable makeObjectsPerformSelector: @selector(setLastModified:) withObject: [NSDate date]]; 
} 
@end 

I (예를 들어 기본 키 몇 가지 다른 방법으로) 사용합니다. 사실

+0

나는 좋아한다! 이런 식으로 뭔가를 찾고 있었습니까 - 샘플 코드를 고맙게 생각합니다. [load super]에게 전화해야합니까?(NSManagedObject 또는 그 부모가 아무 것도하지 않으면 의심된다.) –

+1

OK! 나는 이것을 시도하고, 너무 지나치게 (또는 나는 그것을 오용하고있는 것 같다) 보인다. lastModified (위에서 MYAPPTrackedManagedObject, 기본적으로 TrackedEntity를 통해)를 사용하는 관리 객체가 있습니다. 그러나이 객체는 lastModified를 사용하지 않는 다른 객체와의 관계를 포함합니다. 따라서 setLastModified :는 다른 객체에 의해 인식되지 않으며 예외가 발생합니다. 어쩌면 다시 전화 할 필요가 있을까요? –

+0

아, 아마도 allModified를 걸치고 setLastModified에 응답하지 않는 항목을 걸러 낸 다음 사용하십시오. –

9

또한

- (void)willSave 
{ 
    if (![self isDeleted]) 
    { 
     [self setPrimitiveValue:[NSDate date] forKey:@"updatedAt"]; 
    } 
    [super willSave]; 
} 

`

NSManagedObject's Documentation

`

에 제안 허용 대답은 개체가 표시되어 있는지 여부를 확인, 원시적 인 접근을 사용하는 것보다 훨씬 더 좋은 방법 -isDeleted을 사용하여 삭제하면 -willSave이 호출됩니다.

+8

willsave를 오버라이드 (override)하는 것에 관해서는이 문서가 언급하고 있습니다 : 「원시 액세서를 사용해 프롭퍼티 값을 변경하면 (자), 무한 재귀의 가능성은 피할 수 있습니다 만, Core Data는 변경을 통지하지 않습니다.」 –

+0

@PietroRea가 데이터베이스에 저장 될 예정입니까? – Andy

6

이미이 질문에 대한 여러 가지 좋은 해결책이 있지만 분명히 직면 한 특정 시나리오에서 가장 효과가있는 새로운 것을 던지고 싶었습니다. (스위프트에서

:

override func willSave() { 
    if self.changedValues()["modificationDate"] == nil { 
     self.modificationDate = NSDate() 
    } 

    super.willSave() 
} 

가끔 수동으로 modificationDate을 설정할 필요의 독특한 요구 사항을 가지고 있기 때문에 나는이 필요한 이유입니다. (필자는 서버의 타임 스탬프와 동기화를 유지하려고하기 때문에 가끔 수동으로 타임 스탬프를 설정 한 이유입니다.)

이 솔루션 :

  1. 무한 willSave() 루프 때문에 한 번 방지 타임 스탬프는
  2. 이 타임 스탬프는 modificationDate이 장에있는 경우도 확인할 필요가 수동으로
+0

흥미 롭습니다. 우리는 서버에서 중요한 모든 클라이언트 타임 스탬프를 사용하지 않기 위해 노력하고 있습니다. 장치 클록은 신뢰할 수없는 것일 수 있으므로 동기화 알고리즘에서 순서가 잘못된 오류가 발생할 수 있습니다. 일반적으로 클라이언트에 'lastSynced'값을 저장하고 변경된 객체 만 서버에 요청하는 데 사용합니다. – Sam

+0

@Sam, 나는 이것을 편집하는 사용자가 병합 충돌을 해결할 수있는 * 권한을 가지고있는 데이터에 이것을 사용하고있다. 해결할 UI는 제공하지 않지만 시간 스탬프를 사용하여 충돌을 자동으로 해결합니다. 사용자가이 데이터에 대한 권한을 가지고 있기 때문에 시계가 잘못되었을 수도 있습니다. 그것은 내 사용자를 귀찮게 할지도 모르지만, 아마 내 약속을 잊어 버릴 수도 있습니다. :). 그러나 귀하의 요점은 유효하며 공개적으로 소유 한 데이터에 대해서는이 방법을 사용하지 않을 것이라고 생각합니다. –