Re: Core Data faulting and bindings: recursive KVO notifications?
Re: Core Data faulting and bindings: recursive KVO notifications?
- Subject: Re: Core Data faulting and bindings: recursive KVO notifications?
- From: Ben Trumbull <email@hidden>
- Date: Fri, 28 Mar 2008 22:08:16 -0700
On Mar 28, 2008, at 7:38 PM, Dennis Lorson wrote:
On 28 Mar 2008, at 20:24, Ben Trumbull wrote:
The problem I'm having arises when selecting all images at once
(Command+A), if they are fairly large in number (in my store:
~2000).
This takes an enormous time that is definitely > O(n).
Command-A should be instantaneous, even for selections 10x larger.
The typical performance problems I've seen with array controller
selections are (1) complex KVO observer actions cascade (2)
tripping faults unnecessarily (3) using expensive array controller
options.
(1) Complex KVO observer actions need to be carefully constrained.
If they simply grow organically, then it's pretty easy to fall into
the trap of observers creating side effects that trigger other
observers that cascade to yet more observers. This (a) makes your
code impossible to understand, since no one ever intentionally
designed their app that way from the beginning, and (b) sucks for
performance.
This seems like an easy mistake indeed. However, I checked my code
for this when originally debugging the issue, and while it is
difficult to eliminate all cases, no custom KVO observer triggers
other observers as for my subclass implementation -- and if they do,
they relate to disjunct entity objects that should not trigger others.
You've bound the text fields to the selection poorly. That's the
easiest and best fix. Other workarounds for your edification below.
The good news is that the issue is entirely avoidable by batch
faulting using the 10.5 [NSFetchRequest
setReturnsObjectsAsFaults:NO] and executing the fetch on all objects
in the selection.
Yes. You can also do something like this to ignore KVO notifications
around faulting:
#import <Cocoa/Cocoa.h>
@interface SubObject : NSManagedObject {
@private
BOOL _isAccessible;
}
- (BOOL) isAccessible;
@end
@implementation SubObject
- (void) awakeFromInsert {
[super awakeFromInsert];
_isAccessible = YES;
}
- (void) awakeFromFetch {
[super awakeFromFetch];
_isAccessible = YES;
}
- (void) willTurnIntoFault {
[super willTurnIntoFault];
_isAccessible = NO;
}
- (void) didTurnIntoFault {
[super didTurnIntoFault];
_isAccessible = NO;
}
- (BOOL) isAccessible {
return _isAccessible;
}
@end
and elsewhere:
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:
(id)object change:(NSDictionary *)change context:(void *)context
{
if ([object isKindOfClass:[SubObject class]]) {
SubObject* mine = (SubObject*)object;
if (![mine isAccessible]) {
return;
}
}
// stuff
}
But still, I have two questions:
- Am I overlooking something obvious? If it is the correct
approach, someone else must have experienced this in a larger master-
detail set, right?
Yes, you should disable "Allows Editing Multiple Values Selection"
when you expect selection to include more than a handful of objects.
- While I understand the constraints the CD API imposes, shouldn't
this be labeled as a bug? I'm not sure here, if so, I'll file it
with bugreporter.
You should file a bug and attach your sample app as well.
(2) Firing faults unnecessarily is the canonical performance issue
for Core Data developers. Our SQL logging (-
com.apple.CoreData.SyntaxColoredLogging 1 -
com.apple.CoreData.SQLDebug 1) and our Instruments template can
help you find this, and identify which entities you're faulting
excessively.
If you need that data, it's much (10-100x) better to batch fetch
the data instead. This is described in the Core Data Programming
Guide under Performance.
While I'm certain to perform some optimization here, the delay I was
having was almost fully due to the issue above.
It is indeed very useful, but in my case mostly to avoid the
critical stack overflow. I will not be having 100.000+ stores so
the excessive faulting has still little impact.
Each fault is an I/O. Do a few batches v.s. 1,000 individual ones is
much much better. You can see this in Instruments Core Data template,
with the Cache Miss instrument.
If it's actually recursing through the entire graph, then it's
probably a bug in your observer method. If it's looping, then it
could simply be toggling off a few array controller options will
address the issue.
You should file a bug with bugreport.apple.com, and include the
entire stack trace. Without the rest of it, it's hard for me to
say if this is the expected stack depth or not. It may be a
problem with one of your custom observation methods not coexisting
happily with faulting, but it's a bit hard to tell from this excerpt.
As you can verify from the test project, I don't use any custom KVO
logic. Still, the crash should occur almost every time, after a
variable amount of time (seconds range).
Yes, thanks. Sample projects are terrific. So this project suffers
from both the KVO faulting issue, and a poorly bound text field.
Binding to .selection.a is going to call -valueForKeyPath:@"a" on the
array that's selected, which is 10,000 objects. Since the text field
can only show one of those values, this is kinda pointless. This is
the source of the recursion. Selection is based on the entire array,
which doesn't play well with KVO notifications during faulting.
If you go into the Text Field's binding (cmd-4) and disable "Allows
Editing Multiple Values Selection" then the detail fields go blank
with multiple selections, but work as expected with a single selection.
And cmd-a is instantaneous, even without any of the other workarounds
we've discussed.
I'll file a bug if the issue is determined to be not on my part.
Please file a bug and attach your sample.
- Ben
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden