Two simple questions regarding Core-Data
-
Hello List,
I'm playinig with core-data framework; it's looks really good but I've
two simple questions:
a) is possible to set the value of an attribute with a predicate? For
example I would to set the 'label' attribute to 1 for all entities
into the managed objects. Is the only way to get the complete array of
these entities and then apply the attribute to each manually"?
b) is possible to get the number of items given from a predicate
results instead of using [resultsArray count]? (a sort of count(*) for
sql, I don't need of the list). Or there is not any issue on general
performances? This action will be repeated lots of time in my app.
Thanks a lot
Michael. -
On 5/4/07, Hell's KItchen Hell's KItchen <thehellskitchen...> wrote:> a) is possible to set the value of an attribute with a predicate? For
> example I would to set the 'label' attribute to 1 for all entities
> into the managed objects. Is the only way to get the complete array of
> these entities and then apply the attribute to each manually"?
No, but you can retrieve them into an array and then call
- (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)anObject
on the array. This avoids having to loop over the array and call the
method on each object individually.> b) is possible to get the number of items given from a predicate
> results instead of using [resultsArray count]? (a sort of count(*) for
> sql, I don't need of the list). Or there is not any issue on general
> performances? This action will be repeated lots of time in my app.
Again no. Fortunately when the array is retrieved all of the objects
are "faults" which means they are not loaded into memory yet and your
call to count will not cause them to load. Therefore the memory and
performance hits are negligible.
Marcus S. Zarra
Zarra Studios LLC
Simply Elegant Software for OS X
www.zarrastudios.com -
First, it's a common tendency to think about Core Data as if it were
a database. Here is a short excerpt from the Core Data Programming
Guild:
------------------------------------------
What Core Data Is Not
Having given an overview of what Core Data is, it is also useful to
correct some misperceptions and state what it is not.
Core Data is not a database. Core Data provides an infrastructure for
change management and for saving objects to and retrieving them from
persistent storage. It is not in and of itself a database.
------------------------------------------> a) is possible to set the value of an attribute with a predicate? For
> example I would to set the 'label' attribute to 1 for all entities
> into the managed objects. Is the only way to get the complete array of
> these entities and then apply the attribute to each manually"?
As far as I know the answer is basically yes. You are making a
change to each object, there may be other objects using the value
that is changed that need to be notified of the change through KVO.
You don't want the objects being mutated during a fetch request.> b) is possible to get the number of items given from a predicate
> results instead of using [resultsArray count]? (a sort of count(*) for
> sql, I don't need of the list). Or there is not any issue on general
> performances? This action will be repeated lots of time in my app.
Again, this is why I mentioned the excerpt from the Core Data
Programming Guide. My suggestion would be to start your work with
Core Data using the XML data store. This should help you break the
habit of thinking about Core Data like it was a database. In the
case of the XML or binary persistent stores the entire set of data is
loaded into memory when the file is first opened. Then the entire
store gets replaced, atomically, during a save operation. In this
case it become obvious that you count the objects in the array.
A common design pattern when counting a to-many relation is to cache
the count value within an attribute of the source object.
For example if you have the relation Department < 1----------* >
Person then you could add a "personCount" attribute to Department and
write code in Department to observe changes to the people relation to
update your "counter cache." In this case your count operation
would have no need to fire the Person fault simply to get the count
of people within the department.
On May 4, 2007, at 7:25 AM, Hell's KItchen Hell's KItchen wrote:> Hello List,
> I'm playinig with core-data framework; it's looks really good but I've
> two simple questions:
> a) is possible to set the value of an attribute with a predicate? For
> example I would to set the 'label' attribute to 1 for all entities
> into the managed objects. Is the only way to get the complete array of
> these entities and then apply the attribute to each manually"?
>
> b) is possible to get the number of items given from a predicate
> results instead of using [resultsArray count]? (a sort of count(*) for
> sql, I don't need of the list). Or there is not any issue on general
> performances? This action will be repeated lots of time in my app.
>
> Thanks a lot
> Michael.
--
Robert Walker
<robertwalker1...> -
Marcus,> Again no. Fortunately when the array is retrieved all of the objects
> are "faults" which means they are not loaded into memory yet and your
> call to count will not cause them to load. Therefore the memory and
> performance hits are negligible.
Are you sure that asking for the count would not cause the fault
representing a collection to fire? If so that's really impressive.
How would Core Data know the number of objects represented by the
fault without realizing the fault first?
--
Robert Walker
<robertwalker1...> -
Basically, the fetch will return an array of faults if the objects are
not already in memory. Performing a count on the array will not cause
the faults to fire since you are not accessing the attributes of the
objects inside of the array. You are just asking the array how many
objects it holds.
Personally, I would recommend this method over keeping a count
attribute in a parent object. It is way too easy for that count to
get out of sync with the array. Since counting the number objects in
the relationship will not cause the faults to fire it is safer to get
this information each time it is requested.
Marcus S. Zarra
Zarra Studios LLC
Simply Elegant Software for OS X
www.zarrastudios.com
On 5/4/07, Robert Walker <robertwalker1...> wrote:> Marcus,
>
>> Again no. Fortunately when the array is retrieved all of the objects
>> are "faults" which means they are not loaded into memory yet and your
>> call to count will not cause them to load. Therefore the memory and
>> performance hits are negligible.
>
> Are you sure that asking for the count would not cause the fault
> representing a collection to fire? If so that's really impressive.
> How would Core Data know the number of objects represented by the
> fault without realizing the fault first?
>
> --
> Robert Walker
> <robertwalker1...>
> -
Marcus,
Ok for my own sanity and full understanding of this, I created a very
simple Core Data application with a button connected to an action
used to count people in a department:
Here's my counting code:
01 (IBAction)countPeople:sender
02 {
03 NSManagedObject *selection = [[arrayController selectedObjects]
objectAtIndex:0];
04 NSLog(@"\n\nDepartment before counting: %@", [selection
description]);
05
06 int count = [[selection valueForKey:@"people"] count];
07
08 NSLog(@"\n\nCount of people: %d", count);
09 NSLog(@"\n\nDepartment after counting: %@", [selection
description]);
10 }
And here are the results:
[Session started at 2007-05-04 15:55:51 -0400.]
2007-05-04 15:55:56.931 FaultTest[3015]
Department before counting: <NSManagedObject: 0x320b00> (entity:
Department; id: 0x320dd0 <x-coredata://9C7E671E-A376-4ED1-AB42-
AC0F88013CE6/Department/p1> ; data: {name = "Department A"; people =
"<relationship fault: 0x320ff0 'people'>"; })
2007-05-04 15:55:56.933 FaultTest[3015]
Count of people: 2
2007-05-04 15:55:56.934 FaultTest[3015]
Department after counting: <NSManagedObject: 0x320b00> (entity:
Department; id: 0x320dd0 <x-coredata://9C7E671E-A376-4ED1-AB42-
AC0F88013CE6/Department/p1> ; data: {
name = "Department A";
people = (
0x31b2f0 <x-coredata://9C7E671E-A376-4ED1-AB42-AC0F88013CE6/
Person/p4>,
0x31c250 <x-coredata://9C7E671E-A376-4ED1-AB42-AC0F88013CE6/
Person/p1>
);
})
Does this not indicate that accessing the count of people, through
the relationship on a department, does indeed fire the fault? Or is
there another way of getting the count without using KVC to access
the "people" relationship on a department (as shown in line 6)?
On May 4, 2007, at 1:03 PM, Marcus S. Zarra wrote:> Basically, the fetch will return an array of faults if the objects are
> not already in memory. Performing a count on the array will not cause
> the faults to fire since you are not accessing the attributes of the
> objects inside of the array. You are just asking the array how many
> objects it holds.
>
> Personally, I would recommend this method over keeping a count
> attribute in a parent object. It is way too easy for that count to
> get out of sync with the array. Since counting the number objects in
> the relationship will not cause the faults to fire it is safer to get
> this information each time it is requested.
>
> Marcus S. Zarra
> Zarra Studios LLC
> Simply Elegant Software for OS X
> www.zarrastudios.com
>
>
> On 5/4/07, Robert Walker <robertwalker1...> wrote:
>> Marcus,
>>
>>> Again no. Fortunately when the array is retrieved all of the
>> objects
>>> are "faults" which means they are not loaded into memory yet and
>> your
>>> call to count will not cause them to load. Therefore the memory
>> and
>>> performance hits are negligible.
>>
>> Are you sure that asking for the count would not cause the fault
>> representing a collection to fire? If so that's really impressive.
>> How would Core Data know the number of objects represented by the
>> fault without realizing the fault first?
>>
>> --
>> Robert Walker
>> <robertwalker1...>
>>
>> -
Interesting that your example causes the faults to fire. By removing
the bindings from the equation I was not able to duplicate your
results.
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:@"Invoice"
inManagedObjectContext:managedObjectContext]];
[request setPredicate:[NSPredicate predicateWithFormat:@"invoiceNumber == 19"]];
NSError *errord;
NSArray *array = [managedObjectContext executeFetchRequest:request
error:&errord];
if (!array) {
NSLog(@"Error: %@", errord);
return;
}
NSLog(@"Count of invoices: %d\n%@", [array count], array);
id invoice = [array objectAtIndex:0];
id detailSet = [invoice valueForKey:@"invoiceDetails"];
NSLog(@"Count of details: %d\n%@", [detailSet count], detailSet);
--------Output
2007-05-04 14:44:15.634 seSales[25169] Count of invoices: 1
(
<InvoiceEntity: 0x4e82c0> (entity: Invoice; id: 0x5d37860
<x-coredata://799303D2-FFF6-409B-BA4A-396CE56E3864/Invoice/p334> ;
data: <fault>)
)
2007-05-04 14:44:15.650 seSales[25169] Count of details: 5
<_NSFaultingMutableSet: 0x5b33930> (<InvoiceDetailEntity: 0x5d9efb0>
(entity: InvoiceDetail; id: 0x4f1da0
<x-coredata://799303D2-FFF6-409B-BA4A-396CE56E3864/InvoiceDetail/p1230>
; data: <fault>), <InvoiceDetailEntity: 0x4ce240> (entity:
InvoiceDetail; id: 0x4f1dc0
<x-coredata://799303D2-FFF6-409B-BA4A-396CE56E3864/InvoiceDetail/p2140>
; data: <fault>), <InvoiceDetailEntity: 0x5d29320> (entity:
InvoiceDetail; id: 0x4f1d50
<x-coredata://799303D2-FFF6-409B-BA4A-396CE56E3864/InvoiceDetail/p1210>
; data: <fault>), <InvoiceDetailEntity: 0x4cd2b0> (entity:
InvoiceDetail; id: 0x4f1de0
<x-coredata://799303D2-FFF6-409B-BA4A-396CE56E3864/InvoiceDetail/p2217>
; data: <fault>), <InvoiceDetailEntity: 0x4ce200> (entity:
InvoiceDetail; id: 0x5d9e190
<x-coredata://799303D2-FFF6-409B-BA4A-396CE56E3864/InvoiceDetail/p452>
; data: <fault>))
As you can see I am accessing the invoiceDetails through the
relationship and in my output I do not get the items fired. So this
may indicate that the array controller is giving you a proxy object in
your example or some other magic is going on behind the scenes.
NOTE: the OP was looking for a way to perform a "select count(*) from
xxx" which the above code does perform without a performance hit of
firing the faults.
Marcus S. Zarra
Zarra Studios LLC
Simply Elegant Software for OS X
www.zarrastudios.com
On 5/4/07, Robert Walker <robertwalker1...> wrote:> Marcus,
>
> Ok for my own sanity and full understanding of this, I created a very
> simple Core Data application with a button connected to an action
> used to count people in a department:
>
> Here's my counting code:
>
> 01 (IBAction)countPeople:sender
> 02 {
> 03 NSManagedObject *selection = [[arrayController selectedObjects]
> objectAtIndex:0];
> 04 NSLog(@"\n\nDepartment before counting: %@", [selection
> description]);
> 05
> 06 int count = [[selection valueForKey:@"people"] count];
> 07
> 08 NSLog(@"\n\nCount of people: %d", count);
> 09 NSLog(@"\n\nDepartment after counting: %@", [selection
> description]);
> 10 }
>
> And here are the results:
>
> [Session started at 2007-05-04 15:55:51 -0400.]
> 2007-05-04 15:55:56.931 FaultTest[3015]
>
> Department before counting: <NSManagedObject: 0x320b00> (entity:
> Department; id: 0x320dd0 <x-coredata://9C7E671E-A376-4ED1-AB42-
> AC0F88013CE6/Department/p1> ; data: {name = "Department A"; people =
> "<relationship fault: 0x320ff0 'people'>"; })
> 2007-05-04 15:55:56.933 FaultTest[3015]
>
> Count of people: 2
> 2007-05-04 15:55:56.934 FaultTest[3015]
>
> Department after counting: <NSManagedObject: 0x320b00> (entity:
> Department; id: 0x320dd0 <x-coredata://9C7E671E-A376-4ED1-AB42-
> AC0F88013CE6/Department/p1> ; data: {
> name = "Department A";
> people = (
> 0x31b2f0 <x-coredata://9C7E671E-A376-4ED1-AB42-AC0F88013CE6/
> Person/p4>,
> 0x31c250 <x-coredata://9C7E671E-A376-4ED1-AB42-AC0F88013CE6/
> Person/p1>
> );
> })
>
> Does this not indicate that accessing the count of people, through
> the relationship on a department, does indeed fire the fault? Or is
> there another way of getting the count without using KVC to access
> the "people" relationship on a department (as shown in line 6)?
>
> On May 4, 2007, at 1:03 PM, Marcus S. Zarra wrote:
>
>> Basically, the fetch will return an array of faults if the objects are
>> not already in memory. Performing a count on the array will not cause
>> the faults to fire since you are not accessing the attributes of the
>> objects inside of the array. You are just asking the array how many
>> objects it holds.
>>
>> Personally, I would recommend this method over keeping a count
>> attribute in a parent object. It is way too easy for that count to
>> get out of sync with the array. Since counting the number objects in
>> the relationship will not cause the faults to fire it is safer to get
>> this information each time it is requested.
>>
>> Marcus S. Zarra
>> Zarra Studios LLC
>> Simply Elegant Software for OS X
>> www.zarrastudios.com
>>
>>
>> On 5/4/07, Robert Walker <robertwalker1...> wrote:
>>> Marcus,
>>>
>>>> Again no. Fortunately when the array is retrieved all of the
>>> objects
>>>> are "faults" which means they are not loaded into memory yet and
>>> your
>>>> call to count will not cause them to load. Therefore the memory
>>> and
>>>> performance hits are negligible.
>>>
>>> Are you sure that asking for the count would not cause the fault
>>> representing a collection to fire? If so that's really impressive.
>>> How would Core Data know the number of objects represented by the
>>> fault without realizing the fault first?
>>>
>>> --
>>> Robert Walker
>>> <robertwalker1...>
>>>
>>> -
Marcus,
Thanks for check into this for me. I do realize the OP's question.
My previous post was for my own desire to understand the internal
mechanics of Core Data. I'm really just looking to crack open that
"black box" a bit to rise to the next level of understanding.
It is very interesting that it made a difference fetching the objects
in code with a fetch request, as opposed to pulling the selection out
of the NSArrayController. It would be interesting to know for sure
why that is. Now that I have your example I'll do some more
experimenting on my own. I quite enjoy puzzles like these.
On May 4, 2007, at 5:08 PM, Marcus S. Zarra wrote:> Interesting that your example causes the faults to fire. By removing
> the bindings from the equation I was not able to duplicate your
> results.
>
> NOTE: the OP was looking for a way to perform a "select count(*) from
> xxx" which the above code does perform without a performance hit of
> firing the faults.
>
> Marcus S. Zarra
> Zarra Studios LLC
> Simply Elegant Software for OS X
> www.zarrastudios.com
>
--
Robert Walker
<robertwalker1...> -
Maybe the array controller sorted the array and thus had to access
each object causing it to fault?
On 4-May-07, at 2:48 PM, Robert Walker wrote:> Marcus,
>
> Thanks for check into this for me. I do realize the OP's
> question. My previous post was for my own desire to understand the
> internal mechanics of Core Data. I'm really just looking to crack
> open that "black box" a bit to rise to the next level of
> understanding.
>
> It is very interesting that it made a difference fetching the
> objects in code with a fetch request, as opposed to pulling the
> selection out of the NSArrayController. It would be interesting to
> know for sure why that is. Now that I have your example I'll do
> some more experimenting on my own. I quite enjoy puzzles like these.
>
> On May 4, 2007, at 5:08 PM, Marcus S. Zarra wrote:
>
>> Interesting that your example causes the faults to fire. By removing
>> the bindings from the equation I was not able to duplicate your
>> results.
>>
>> NOTE: the OP was looking for a way to perform a "select count(*) from
>> xxx" which the above code does perform without a performance hit of
>> firing the faults.
>>


