Core Data Confusion
-
For the last couple of weeks I have been navigating through the Core
Data swamp and have made significant progress in my understanding of
the architecture. That said, there are a couple of things that
really have me shaking my head, and I would appreciate a second look.
Real quick: my application is intended to track information about
animals on different farms. The data model has three entities:
- FARM <-->> ANIMAL <-->> VACCINATION
Note the relationships between each entity are to-many going in the
right direction and to-one going in the left (inverse) direction. A
farm can contain many animals and each animal can have multiple
vaccinations.
Without getting into too much detail, the FARM entity has a transient
property report(a string) which depends on other attributes in the
FARM entity changes. So the initialize method in my FARM Managed
Object class is:
+ (void)initialize
{
[self setKeys:[NSArray arrayWithObjects:
@"farmName",
@"managerName",
nil]
triggerChangeNotificationsForDependentKey:@"report"];
}
The attributes farmName and managerName are updated in text fields in
a window and are updated daily. The idea is that when either of
these values change, the report attribute should be updated to
reflect changes to the names. My report method:
- (NSString *)report
{
NSString * tmpValue = nil;
[self willAccessValueForKey: @"report"];
tmpValue = [self constructFarmReport];
[self didAccessValueForKey: @"report"];
return tmpValue;
}
- (NSString *)constructFarmReport
{
NSString *reportString = [[NSString alloc] initWithString:@""];
NSString *farm = [self primitiveValueForKey:@"farmName"];
NSString *manager = [self primitiveValueForKey:@"managerName"];
...
When the report method is called due to a change in farmName/
managerName, it will call constructFarmReport which builds the report
string and returns it. Note that I am using the method
primitiveValueForKey to access the values farmName/managerName
instead of accessing the identifiers directly. I've found that in
the debugger, if I don't use primitiveValueForKey, then the
willAccessValueForKey/didAccessValueForKey methods get invoked. My
question is: is using the primitive method to obtain the values of
these attributes a bad thing? I know the Core Data document
discourages one from using the primitive version of the call, and
encourages use of either valueForKey: or access the identifier
directly, but if control is already in a method which was invoked
BECAUSE of KVO, is accessing that variable again INSIDE of the
observed method a bad thing?
--
Boisy G. Pitre
337.781.3570 mobile
email: <boisy...>
Website: www.boisypitre.com
"If there is truth to the proposition that knowing the past helps us
to understand the present, I believe there is at least as much truth
to the proposition that what we know of the present is crucial to our
understanding of the past. What we have not ourselves experienced or
observed we can at most only partially and imperfectly comprehend;
and I suspect that there is much in history that is so remote from
our own experiences or observations as to be largely beyond our
understanding." - Kenneth M. Stamp -
Short answer is no
It is not a bad thing to be accessing the primitive values in this
situation. I believe that warning was for external access to the
values. e.g. object A should be calling -getValueForKey: instead of -
getPrimitiveValueForKey: on object B. This ensures that object B has
complete control over what happens when that value is accessed.
Marcus S. Zarra
Zarra Studios LLC
Simply Elegant Software for OS X
www.zarrastudios.com
On Jan 31, 2007, at 10:32 AM, Boisy Pitre wrote:> For the last couple of weeks I have been navigating through the
> Core Data swamp and have made significant progress in my
> understanding of the architecture. That said, there are a couple
> of things that really have me shaking my head, and I would
> appreciate a second look.
>
> Real quick: my application is intended to track information about
> animals on different farms. The data model has three entities:
>
> - FARM <-->> ANIMAL <-->> VACCINATION
>
> Note the relationships between each entity are to-many going in the
> right direction and to-one going in the left (inverse) direction.
> A farm can contain many animals and each animal can have multiple
> vaccinations.
>
> Without getting into too much detail, the FARM entity has a
> transient property report(a string) which depends on other
> attributes in the FARM entity changes. So the initialize method in
> my FARM Managed Object class is:
>
> + (void)initialize
> {
> [self setKeys:[NSArray arrayWithObjects:
> @"farmName",
> @"managerName",
> nil]
> triggerChangeNotificationsForDependentKey:@"report"];
> }
>
> The attributes farmName and managerName are updated in text fields
> in a window and are updated daily. The idea is that when either of
> these values change, the report attribute should be updated to
> reflect changes to the names. My report method:
>
> - (NSString *)report
> {
> NSString * tmpValue = nil;
>
> [self willAccessValueForKey: @"report"];
> tmpValue = [self constructFarmReport];
> [self didAccessValueForKey: @"report"];
>
> return tmpValue;
> }
>
>
> - (NSString *)constructFarmReport
> {
> NSString *reportString = [[NSString alloc] initWithString:@""];
> NSString *farm = [self primitiveValueForKey:@"farmName"];
> NSString *manager = [self primitiveValueForKey:@"managerName"];
> ...
>
> When the report method is called due to a change in farmName/
> managerName, it will call constructFarmReport which builds the
> report string and returns it. Note that I am using the method
> primitiveValueForKey to access the values farmName/managerName
> instead of accessing the identifiers directly. I've found that in
> the debugger, if I don't use primitiveValueForKey, then the
> willAccessValueForKey/didAccessValueForKey methods get invoked. My
> question is: is using the primitive method to obtain the values of
> these attributes a bad thing? I know the Core Data document
> discourages one from using the primitive version of the call, and
> encourages use of either valueForKey: or access the identifier
> directly, but if control is already in a method which was invoked
> BECAUSE of KVO, is accessing that variable again INSIDE of the
> observed method a bad thing?
> --
> Boisy G. Pitre
> 337.781.3570 mobile
> email: <boisy...>
> Website: www.boisypitre.com
>
> "If there is truth to the proposition that knowing the past helps
> us to understand the present, I believe there is at least as much
> truth to the proposition that what we know of the present is
> crucial to our understanding of the past. What we have not
> ourselves experienced or observed we can at most only partially and
> imperfectly comprehend; and I suspect that there is much in history
> that is so remote from our own experiences or observations as to be
> largely beyond our understanding." - Kenneth M. Stamp
>
>
>
> _______________________________________________
> MacOSX-dev mailing list
> <MacOSX-dev...>
> http://www.omnigroup.com/mailman/listinfo/macosx-dev -
On Feb 2, 2007, at 2:05 PM, Marcus S. Zarra wrote:> It is not a bad thing to be accessing the primitive values in this
> situation. I believe that warning was for external access to the
> values. e.g. object A should be calling -getValueForKey: instead
> of -getPrimitiveValueForKey: on object B. This ensures that object
> B has complete control over what happens when that value is accessed.
What do you think about the fact that -primitiveValueForKey isn't
wrapped in will/did accessValueForKey? That seems like it could cause
some bizarre behavior.
- Scott -
On 31 Jan 2007, at 6:32 PM, Boisy Pitre wrote:> For the last couple of weeks I have been navigating through the
> Core Data swamp and have made significant progress in my
> understanding of the architecture. That said, there are a couple
> of things that really have me shaking my head, and I would
> appreciate a second look.
>
> Real quick: my application is intended to track information about
> animals on different farms. The data model has three entities:
>
> - FARM <-->> ANIMAL <-->> VACCINATION
>
> Note the relationships between each entity are to-many going in the
> right direction and to-one going in the left (inverse) direction.
> A farm can contain many animals and each animal can have multiple
> vaccinations.
>
> Without getting into too much detail, the FARM entity has a
> transient property report(a string) which depends on other
> attributes in the FARM entity changes. So the initialize method in
> my FARM Managed Object class is:
>
> + (void)initialize
> {
> [self setKeys:[NSArray arrayWithObjects:
> @"farmName",
> @"managerName",
> nil]
> triggerChangeNotificationsForDependentKey:@"report"];
> }
>
> The attributes farmName and managerName are updated in text fields
> in a window and are updated daily. The idea is that when either of
> these values change, the report attribute should be updated to
> reflect changes to the names. My report method:
>
> - (NSString *)report
> {
> NSString * tmpValue = nil;
>
> [self willAccessValueForKey: @"report"];
> tmpValue = [self constructFarmReport];
> [self didAccessValueForKey: @"report"];
>
> return tmpValue;
> }
>
>
> - (NSString *)constructFarmReport
> {
> NSString *reportString = [[NSString alloc] initWithString:@""];
> NSString *farm = [self primitiveValueForKey:@"farmName"];
> NSString *manager = [self primitiveValueForKey:@"managerName"];
> ...
>
> When the report method is called due to a change in farmName/
> managerName, it will call constructFarmReport which builds the
> report string and returns it. Note that I am using the method
> primitiveValueForKey to access the values farmName/managerName
> instead of accessing the identifiers directly. I've found that in
> the debugger, if I don't use primitiveValueForKey, then the
> willAccessValueForKey/didAccessValueForKey methods get invoked. My
> question is: is using the primitive method to obtain the values of
> these attributes a bad thing? I know the Core Data document
> discourages one from using the primitive version of the call, and
> encourages use of either valueForKey: or access the identifier
> directly, but if control is already in a method which was invoked
> BECAUSE of KVO, is accessing that variable again INSIDE of the
> observed method a bad thing?
Why would it be called JUST because of KVO? If you use the report
property anywhere, it could just be called because the report value
is needed for (inital) display or something. Generally, when you
access a variable in CD you should use valueForKey or call
willAccessValueForKey/didAccessForKey, I don't see any reason why
this situation is any different. I can imagine that CD has to fetch
or update the value for farmName or managerName (I don't really know
how willAccessValueForKey/didAccessForKey are used, but they can do
something).
Christiaan> --
> Boisy G. Pitre
> 337.781.3570 mobile
> email: <boisy...>
> Website: www.boisypitre.com
>
> "If there is truth to the proposition that knowing the past helps
> us to understand the present, I believe there is at least as much
> truth to the proposition that what we know of the present is
> crucial to our understanding of the past. What we have not
> ourselves experienced or observed we can at most only partially and
> imperfectly comprehend; and I suspect that there is much in history
> that is so remote from our own experiences or observations as to be
> largely beyond our understanding." - Kenneth M. Stamp
>
>
>
> _______________________________________________
> MacOSX-dev mailing list
> <MacOSX-dev...>
> http://www.omnigroup.com/mailman/listinfo/macosx-dev -
On Jan 31, 2007, at 9:32 AM, Boisy Pitre wrote:> My question is: is using the primitive method to obtain the valuesYes -- it's not clear why you would ignore the advice in the
> of these attributes a bad thing? I know the Core Data document
> discourages one from using the primitive version of the call, and
> encourages use of either valueForKey: or access the identifier
> directly, but if control is already in a method which was invoked
> BECAUSE of KVO, is accessing that variable again INSIDE of the
> observed method a bad thing?
>
documentation? The will/didAccess.. calls are required to fire faults.
mmalc -
I think the OP meant the remark about KVO the other way around: he
seems to think that the report is called only because KVO told that
the underlying data has changed. At least that is what I understood,
and remarked this assumption is generally incorrect.
Generally, when accessing properties managed by CD, one should use
the proper non-primitive methods or wrap will/didAccessValueForKey
methods around the primitive accessors. That's what the docs say, and
I assume that's for a reason. The reason I can think of is that the
will/didAccess.. methods make sure to fire the fault (fetch/update
the value) if necessary. That would make sense to me, knowing that CD
doesn't fetch properties immediately. Anyway, I don't know what it
does, and I don't need to know. The only thing I need to know is what
is in the docs: you better do it make sure the will/didAccess...
methods are called. HOWEVER, if you choose to use primitive
accessors, you are responsible to make sure everything is set up
right. When you also don't use the will/didAccess... methods, you
have to make sure that everything these methods does (like
faulting,etc ?) has been taken care of. This DOES mean you need to
know what these methods do (and you don't, unless you work for Apple,
but also that is not true, as some framework developer might need to
subclass these methods).
So to conclude. Can it hurt? The answer is: Yes.
Christiaan
On 3 Feb 2007, at 8:47 PM, Marcus S. Zarra wrote:> It is true that -valueForKey: does not cause the KVO to fire and
> that the OP could just use that instead of the primitive
> accessors. But it also does not hurt anything, especially if he
> has some other logic sitting in the -(NSString*)reportName; method
> that he does not want to fire.
>
> Marcus S. Zarra
> Zarra Studios LLC
> Simply Elegant Software for OS X
> www.zarrastudios.com
>
> On Feb 2, 2007, at 4:07 PM, Christiaan Hofman wrote:
>
>>
>> On 31 Jan 2007, at 6:32 PM, Boisy Pitre wrote:
>>
>>> For the last couple of weeks I have been navigating through the
>>> Core Data swamp and have made significant progress in my
>>> understanding of the architecture. That said, there are a couple
>>> of things that really have me shaking my head, and I would
>>> appreciate a second look.
>>>
>>> Real quick: my application is intended to track information about
>>> animals on different farms. The data model has three entities:
>>>
>>> - FARM <-->> ANIMAL <-->> VACCINATION
>>>
>>> Note the relationships between each entity are to-many going in
>>> the right direction and to-one going in the left (inverse)
>>> direction. A farm can contain many animals and each animal can
>>> have multiple vaccinations.
>>>
>>> Without getting into too much detail, the FARM entity has a
>>> transient property report(a string) which depends on other
>>> attributes in the FARM entity changes. So the initialize method
>>> in my FARM Managed Object class is:
>>>
>>> + (void)initialize
>>> {
>>> [self setKeys:[NSArray arrayWithObjects:
>>> @"farmName",
>>> @"managerName",
>>> nil]
>>> triggerChangeNotificationsForDependentKey:@"report"];
>>> }
>>>
>>> The attributes farmName and managerName are updated in text
>>> fields in a window and are updated daily. The idea is that when
>>> either of these values change, the report attribute should be
>>> updated to reflect changes to the names. My report method:
>>>
>>> - (NSString *)report
>>> {
>>> NSString * tmpValue = nil;
>>>
>>> [self willAccessValueForKey: @"report"];
>>> tmpValue = [self constructFarmReport];
>>> [self didAccessValueForKey: @"report"];
>>>
>>> return tmpValue;
>>> }
>>>
>>>
>>> - (NSString *)constructFarmReport
>>> {
>>> NSString *reportString = [[NSString alloc] initWithString:@""];
>>> NSString *farm = [self primitiveValueForKey:@"farmName"];
>>> NSString *manager = [self primitiveValueForKey:@"managerName"];
>>> ...
>>>
>>> When the report method is called due to a change in farmName/
>>> managerName, it will call constructFarmReport which builds the
>>> report string and returns it. Note that I am using the method
>>> primitiveValueForKey to access the values farmName/managerName
>>> instead of accessing the identifiers directly. I've found that
>>> in the debugger, if I don't use primitiveValueForKey, then the
>>> willAccessValueForKey/didAccessValueForKey methods get invoked.
>>> My question is: is using the primitive method to obtain the
>>> values of these attributes a bad thing? I know the Core Data
>>> document discourages one from using the primitive version of the
>>> call, and encourages use of either valueForKey: or access the
>>> identifier directly, but if control is already in a method which
>>> was invoked BECAUSE of KVO, is accessing that variable again
>>> INSIDE of the observed method a bad thing?
>>
>>
>> Why would it be called JUST because of KVO? If you use the report
>> property anywhere, it could just be called because the report
>> value is needed for (inital) display or something. Generally, when
>> you access a variable in CD you should use valueForKey or call
>> willAccessValueForKey/didAccessForKey, I don't see any reason why
>> this situation is any different. I can imagine that CD has to
>> fetch or update the value for farmName or managerName (I don't
>> really know how willAccessValueForKey/didAccessForKey are used,
>> but they can do something).
>>
>> Christiaan
>>
>>> --
>>> Boisy G. Pitre
>>> 337.781.3570 mobile
>>> email: <boisy...>
>>> Website: www.boisypitre.com
>>>
>>> "If there is truth to the proposition that knowing the past helps
>>> us to understand the present, I believe there is at least as much
>>> truth to the proposition that what we know of the present is
>>> crucial to our understanding of the past. What we have not
>>> ourselves experienced or observed we can at most only partially
>>> and imperfectly comprehend; and I suspect that there is much in
>>> history that is so remote from our own experiences or
>>> observations as to be largely beyond our understanding." -
>>> Kenneth M. Stamp -
The KVO methods do not fire faults in Core Data. Core Data retrieves
all of the values for a managed object when that object is accessed.
It specifically does not do any lazy initialization of attributes.
However, it does do lazy initialization of relationships.
The primitive methods will cause a fault to fire on a lazy
relationship. If you access another managed object via -
primitiveValueForKey: and that object has not been loaded yet -- the -
primitiveValueForKey: method will cause it to fire. This can be
tested very easily.
The -willChangeValueForKey: and -didChangeValueForKey: should be
wrapped around any calls to primitive setters -- not getters. The
will/did methods cause a KVO notice to be sent out to let other
observers know that the value has changed. It does not make sense to
fire the will/did when you are reading the value. In the example the
OP posted, the will/did are not necessary and accessing the values in
the same managed object via the primitive methods is safe and makes
sense in some situations.
Marcus S. Zarra
Zarra Studios LLC
Simply Elegant Software for OS X
www.zarrastudios.com
On Feb 5, 2007, at 6:40 AM, Christiaan Hofman wrote:> I think the OP meant the remark about KVO the other way around: he
> seems to think that the report is called only because KVO told that
> the underlying data has changed. At least that is what I
> understood, and remarked this assumption is generally incorrect.
>
> Generally, when accessing properties managed by CD, one should use
> the proper non-primitive methods or wrap will/didAccessValueForKey
> methods around the primitive accessors. That's what the docs say,
> and I assume that's for a reason. The reason I can think of is that
> the will/didAccess.. methods make sure to fire the fault (fetch/
> update the value) if necessary. That would make sense to me,
> knowing that CD doesn't fetch properties immediately. Anyway, I
> don't know what it does, and I don't need to know. The only thing I
> need to know is what is in the docs: you better do it make sure the
> will/didAccess... methods are called. HOWEVER, if you choose to use
> primitive accessors, you are responsible to make sure everything is
> set up right. When you also don't use the will/didAccess...
> methods, you have to make sure that everything these methods does
> (like faulting,etc ?) has been taken care of. This DOES mean you
> need to know what these methods do (and you don't, unless you work
> for Apple, but also that is not true, as some framework developer
> might need to subclass these methods).
>
> So to conclude. Can it hurt? The answer is: Yes.
>
> Christiaan -
On 2/5/07, Marcus S. Zarra <mzarra...> wrote:> The KVO methods do not fire faults in Core Data. Core Data retrieves
> all of the values for a managed object when that object is accessed.
> It specifically does not do any lazy initialization of attributes.
> However, it does do lazy initialization of relationships.
>
> The primitive methods will cause a fault to fire on a lazy
> relationship. If you access another managed object via -
> primitiveValueForKey: and that object has not been loaded yet -- the -
> primitiveValueForKey: method will cause it to fire. This can be
> tested very easily.
>
> The -willChangeValueForKey: and -didChangeValueForKey: should be
> wrapped around any calls to primitive setters -- not getters.
...and folks are talking about will/didAccessValueForKey: [1].
For example...
- (Measure *)measure {
id tmpObject;
[self willAccessValueForKey: @"measure"];
tmpObject = [self primitiveValueForKey: @"measure"];
[self didAccessValueForKey: @"measure"];
return tmpObject;
}
- (void)setMeasure:(Measure *)value {
[self willChangeValueForKey: @"measure"];
[self setPrimitiveValue: value forKey: @"measure"];
[self didChangeValueForKey: @"measure"];
}
[1] <http://developer.apple.com/documentation/Cocoa/Reference/CoreDataFramework/
Classes/NSManagedObject_Class/Reference/Reference.html#//apple_ref/occ/inst
m/NSManagedObject/didAccessValueForKey:> -
On 2/5/07, Marcus S. Zarra <mzarra...> wrote:> The KVO methods do not fire faults in Core Data. Core Data retrieves
> all of the values for a managed object when that object is accessed.
> It specifically does not do any lazy initialization of attributes.
> However, it does do lazy initialization of relationships.
I should also note that your are making assumption about how Core Data
will operate... it could change its faulting behavior in the future
(it could lazily load some subset of attributes). Using the correct
will/did Access/Change methods when dealing with primitive values will
protect you from changes in the implementation.
The API and documentation do not preclude that Apple could extend Core
Data in such ways... (in fact the documentation recommendations and
API actually support that type of enhancement)
-Shawn -
On 5 Feb 2007, at 6:19 PM, Shawn Erickson wrote:> On 2/5/07, Marcus S. Zarra <mzarra...> wrote:
>> The KVO methods do not fire faults in Core Data.
So why does the documentation on didAccessValueForKey : say /
explicitly/:
Together with willAccessValueForKey:, this method is used to fire
faults, ...
(see the link in the earlier mail from Shawn Erickson).>> Core Data retrieves
>> all of the values for a managed object when that object is accessed.
>> It specifically does not do any lazy initialization of attributes.
>> However, it does do lazy initialization of relationships.
>
> I should also note that your are making assumption about how Core Data
> will operate... it could change its faulting behavior in the future
> (it could lazily load some subset of attributes). Using the correct
> will/did Access/Change methods when dealing with primitive values will
> protect you from changes in the implementation.
>
> The API and documentation do not preclude that Apple could extend Core
> Data in such ways... (in fact the documentation recommendations and
> API actually support that type of enhancement)
>
> -Shawn
Exactly my point: perhaps it does nothing in some specific cases, but
you cannot rely on that as long the documentation says otherwise. And
as I noted: subclassers could also change that behavior.
Christiaan -
On Feb 5, 2007, at 9:04 AM, Marcus S. Zarra wrote:> The KVO methods do not fire faults in Core Data. [...]
> The primitive methods will cause a fault to fire on a lazy
> relationship.
Even if that's true now (and I don't think it is), it could change in
the future. It's an implementation detail which is encapsulated and
shouldn't be relied on.> The -willChangeValueForKey: and -didChangeValueForKey: should be
> wrapped around any calls to primitive setters -- not getters
True, but these are easily confused with -will__Access__ValueForKey:
and -did__Access__ValueForKey: which do (potentially) fire faults.
There are two sets of will/did notifications:
Getters:
- willAccessValueForKey;
- didAccessValueForKey;
Setters:
- willChangeValueForKey:
- didChangeValueForKey:
I heard your Late Night Cocoa interview* (which was quite good), so
you clearly know what you're talking about. I suspect there was a
typo somewhere in this thread that caused all of this confusion.
Ironically, the word confusion is right in the thread name.
- Scott
(* http://latenightcocoa.com/?q=node/16)


