Re: Core Data: Changing Transient Keys Dirties Database?
Re: Core Data: Changing Transient Keys Dirties Database?
- Subject: Re: Core Data: Changing Transient Keys Dirties Database?
- From: Robert McNally <email@hidden>
- Date: Wed, 22 Jun 2005 13:26:53 -0700
>On Jun 21, 2005, at 9:51 PM, Robert McNally wrote:
>
>>However, I also have another periodic task that changes my application's idea of the "current date" every minute or so-- some of my model objects observe this key and propagate some changes through non-persistent keys of their own as a result. This action should not, but does dirty the database, and causes the first periodic task to rewrite the entire data store when it's not necessary.
>>
>
>Umm, changing a transient property does not "dirty the database". It adds undo events to the undo manager and marks the managed object as updated.
To be clear, what I mean by "dirty the database" is that the list of inserted, updated, and/or deleted objects become non-empty. At least when using the XML data store type, this appears to cause [myManagedObjectContext save:] to re-write the entire data store, even when only transient attributes of managed objects have been changed.
>The point of a transient property is precisely that, although it's not stored in the persistent store, Core Data does track changes and register undo events for the property...
>
><http://developer.apple.com/documentation/Cocoa/Reference/CoreData_ObjC/Classes/NSPropertyDescription.html#//apple_ref/doc/uid/TP30001178-BAJHJGHJ>
That's fine, but the document you refer to says "Transient properties are ignored by the persistent store, and not just during saves...", however this doesn't appear to be the case-- changed transient properties still cause an object to appear in the updated list. In fact, simply calling [myManagedObject willChangeValueForKey:@"foobar"] and then [myManagedObject didChangeValueForKey: @"foobar"] on a key that neither appears in the model nor anywhere else in my application ("foobar" in this example) STILL causes the object to appear in [myManagedObjectContext updatedObjects], and hence causes [myManagedObjectContext save:] to re-write the data store.
>>Finally, I notice that this seems to have something to do with the Undo machinery, because I am able to perform an "undo" immediately after the minute-timer fires, and if I perform that Undo, then the database is no longer dirty. I tried surrounding my willChange/didChange calls
>>
>Umm, what willChange/didChange calls...
[myManagedObject willChangeValueForKey:] and [myManagedObject didChangeValueForKey:]
>>One of my model objects has a number of keys that are either in the model marked as "transient", or aren't in the xcdatamodel file at all but my class has getters and setters for them. These getters and setters either directly or indirectly call "willChangeValueForKey/didChangeValueForKey," but they do not directly nor indirectly update any of the persistent attributes.
>>
>... are you invoking the change notification methods outside the scope described in "Managed Object Accessor Methods" (<http://developer.apple.com/documentation/Cocoa/Conceptual/CoreData/Articles/cdAccessorMethods.html#//apple_ref/doc/uid/TP40002154>)?
>
I carefully re-read the document you refer to, and my accessors all follow the patterns you describe. Possibly the only exception does relate to my question-- let me try to snip out the pattern I'm currently using:
@implementation CTask // Inherits from NSManagedObject
...
// The "currentDate" property of [CAppController appController] changes once per minute. The app controller is just a regular NSObject.
// Neither the "currentDate" nor "overdue" attributes of CTask are declared in the managed object model. "currentDate" follows the app controller, and "overdue" is entirely derived from other attributes
+ (void)initialize
{
[self setKeys:[NSArray arrayWithObject:@"currentDate"] triggerChangeNotificationsForDependentKey:@"overdue"];
}
- (void)awakeFromInsert
{
[super awakeFromInsert];
[[CAppController appController] addObserver:self forKeyPath:@"currentDate" options:nil context:nil];
}
- (void)awakeFromFetch
{
[super awakeFromFetch];
[[CAppController appController] addObserver:self forKeyPath:@"currentDate" options:nil context:nil];
}
- (void)dealloc
{
[[CAppController appController] removeObserver:self forKeyPath:@"currentDate"];
[super dealloc];
}
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
{
if([keyPath isEqualToString:@"currentDate"] && object == [CAppController appController]) {
// The following triggers observers of the "overdue" attribute
// to update their display of this task. It also
// registers a spurious Undo event, and it also spuriously
// adds this task to the set of changed objects in the
// managed object context. I want it only to trigger the
// update notifications for dependent keys.
[self willChangeValueForKey:@"currentDate"];
[self didChangeValueForKey:@"currentDate"];
}
}
...
- (BOOL)overdue
{
NSCalendarDate* dueDate = [self dueDate];
return dueDate != nil && DateIsLaterThanOrEqualToDate([[CAppController appController] currentDate], dueDate) && [self active];
}
...
@end // CTask
>
>>with [myUndoManager disableUndoRegistration/enableUndoRegistration], but that only seems to make the ability to perform the Undo go away-- the database is still dirtied.
>>
>You'll probably also need to invoke processPendingChanges, but it's difficult to say more without knowing exactly what you're doing and why.
Exactly under what circumstances to use processPendingChanges remains a mystery to me. Perhaps the detail I provide above can help you tell me whether this is such a case.
>
>To address other aspects:
>>However, I also have another periodic task that changes my application's idea of the "current date" every minute or so-- some of my model objects observe this key and propagate some changes through non-persistent keys of their own as a result.
>>
>It's not clear that model objects should be observing something in the application. You should probably be sending messages to the model objects from a controller.
While you may be right, as my current pattern causes every fetched task to listen to the "currentDate" attribute of the app controller, I don't see how implementing it differently would result in a fix in my particular situation-- I would still be calling a "currentDateChanged" method on my CTask object that would then have to trigger update notifications in other non-persistent keys of the task, which would seem to cause the problem all over again.
>Is there any reason that the non-persistent keys must be transient?
>Can they be simple instance variables? What role do they play?
Obviously, "currentDate" should not be persistent, as it changes from minute to minute. "overdue" is entirely dependent on "currentDate", "dueDate" (a regular persistent attribute of the task, and "active", which itself is derived from several other task attributes, and which determines whether the task is eligible for various forms of notification, including whether it is "overdue."
Thanks for your help,
Robert
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden