Mailing Lists: Apple Mailing Lists
Image of Mac OS face in stamp
Re: Core Data, transient ivars and undo
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Core Data, transient ivars and undo




On 平成 18/02/14, at 10:53, Christiaan Hofman wrote:


On 14 Feb 2006, at 8:00 PM, Andre wrote:


On 平成 18/02/14, at 1:03, Christiaan Hofman wrote:


On 14 Feb 2006, at 10:27 AM, email@hidden wrote:


On 平成 18/02/13, at 6:43, Christiaan Hofman wrote:


On 13 Feb 2006, at 1:09 AM, email@hidden wrote:

Christiaan Hofman wrote:

I am very much confused about undo in Core Data, in particular when it comes to "transient" values and other dependent state information. AFAICS the transient value or state info is not updated in any way in undo, which can easily lead to inconsistent states in CD apps. How should I handle this in general?

For example a "transient" value stored in an ivar. Eg for a non-standard attribute, which is stored in the MOC as data, but has an ivar representation in a NSManagedObject subclass. Usually, you interact with the transient value (ivar) rather than the underlying CD value. However it is the data in the MOC that is changed in undo, and only that. So you get an inconsistent state. Writing (KVC compliant) accessors for the underlying data does not help, as they are not called by undo.

In the docs and example projects like CoreRecipes many accessors seem to have this flaw. Eg the "bounds" example in "Non-Standard Attributes" section of the Core Data Programming Guide. Here the bounds ivar and the boundsAsData atribute should always be synchronized. However, AFAICS, if I would undo setting (changing) the bounds, only the boundsAsData attribute is changed, giving me an inconsistent state. This seems to me a bug in CD, or at least in the docs and the examples, as nothing is mentioned about it.
Perhaps [self setKey:@"boundsAsData" triggerChangeNotificationForDependentKeys:[NSArray arrayWithObject:@"bounds"]] ?
Since, bounds is not "known" by core data that it is related to boundsAsData, it needs to be told they are, I beleive that telling core data to generate a change note when boundsAsData changes to also notify observers of the change to bounds, would call the getter in your bounds method, and reverse the change.... have you tried this?



No, this would not work. This way just observers are told that bounds has changed as well, however the actual value of the ivar bounds has not changed, which is the real problem. When the getter is called the stale value for bounds is found and returned. No new value is generated as it was not reset (note that it is only regenerated when it has the proper zero value, otherwise it would always be regenerated).
I see, how about in the boundsAsData method, override it to also reset the cached value of bounds so that the next time bounds is called, the cached value is non-existent, then it can regenerate the cache and return it the next time bounds is called? Would that work?

The question is: where should the cached (derived) data be reset? You seem to suggest here to do it in he getter, but that is equivalent to recalculating the derived data every time, making the cache useless, and this can become very inefficient (note that the getter cannot know if it was called after it was invalidate unless it was already invalidated). Doing this in a setter will not work, as undo does not call those. KVO dependent keys don't call anything, they merely notify, so they cannot invalidate the data either. I don't see any other way than KVO observing.
I see... here was what I was thinking. In the setter for boundsAsData, set the value of (cached) bounds (setting the variable directly) to nil. Then, only if the value of the cached bounds is nill, it (the bounds getter) recalculates from the derived value and sets the cache (directly, not using KVC), otherwise it simply calls the cached value. If there are any observers of bounds, then the dependent key notification will let those observers know that when boundsAsData changed, also bounds is changed, and they will pull the data from bounds. If there are no observers, then its OK if the bounds cache is stale, since its nil, only the first time its called, bounds is recalculated from the derived value, then subsequent calls are from the cache, as long as the cache is non-nil. It should be pretty effecient....KVB/ KVO basically encourages the lazy/deferred approach to data management AFIACS.


I think you don't understand the question. In the bounds example, the cached bounds ivar is what I call the derived value. The persistent managed data is the boundsAsData value. In practice most of the app will work directly with the cached ivar.


I don 't think in such a situation, where the persistent value is not directly accessed, there should be a need to implement a custom accessor at all. In fact setting the cache to nil in this setter will be useless, as the setter will never be called (by undo for instance). Therefore the stale value of the cache will _not_ be nil (or an appropriate zero value), which is a problem.
I would say thats a valid argument. And, I have observed this as well, that undo does not seem to call the setter, it just "magically" gets set. Which was why originally I though setting a dependent key for the derived value might help, because the value would have changed via KVO when using undo, even if the accessor is not called, at least in my assumption, though maybe thats mistaken.

So I think either the cache should be a transient attribute in the MOM, as in examples the docs (this is the easy case in which I am not interested).

Or any refreshing of cache values and other dependent data should be done through KVO observation method. I see no other way.

So something like the following:


- (void)refresh { [self willChangeValueForKey:@"dependentValue"]; dependentValue release]; dependentValue = nil; [self didChangeValueForKey:@"dependentValue"]; // this can do more complicated stuff, like refreshing a smart group }

- (id)dependentValue {
	if (dependentValue == nil) {
		dependentValue = [[self recalculateDependentValue] retain];
	}
	return dependentValue;
}

- (void)setDependentValue:(id)value {
NSData *persistentData = [self calculateDataFromDependentValue:value];
[self setValue:persistentData forKey:@"persistentData"];
// the dependent value will be automatically refreshed through KVO
}


- (void)observeValueForKeyPath:(NSString *)keyPath
		ofObject:(id)object
		change:(NSDictionary *)change
		context:(void)context {
	if ([keyPath isEqual:@"persistentData"]) {
		[self refresh];
	}
}


Would this be the basic way to implement this kind of situation?
I think that may be a way, how does it work when you implemented it? Since its not a technique exemplified in the documentation AFAICS, its up to ourselves to discover if its the "right way" or not I would think in this case. Here: http://developer.apple.com/documentation/ Cocoa/Conceptual/CoreData/index.html the only sanctioned way mentioned is with another accessor, and the non-standard attribute to be modeled in the model as an undefined transient attribute...

Here is something I found on something close to this subject: http:// www.awprofessional.com/articles/article.asp?p=432805&seqNum=8&rl=1

I tried to think about the third option, where I implement by hand undo on the derived values. But this has a problem with redo. Or am I wrong?
You could do that, as I have thought about it myself in some situations..... coredata uses the same undo manager as everything else, so that may be a possible way. But there may be some state information in core data that we don't know about, and something that coredata gets undone, then it may not be doing just what we think. I would say, the closest we get to the undo mechanism is using key value change notification for attributes and the change key using set mutation notification for to-many. I haven't seen any articles about manual undo in core data.

If you have any results, please let me know what you find, I'm curious myself.

Andre
email@hidden



_______________________________________________
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


References: 
 >Re: Core Data, transient ivars and undo (From: email@hidden)
 >Re: Core Data, transient ivars and undo (From: email@hidden)
 >Re: Core Data, transient ivars and undo (From: Andre <email@hidden>)



Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Contact Apple | Terms of Use | Privacy Policy

Copyright © 2011 Apple Inc. All rights reserved.