1

I'm encountering a problem where some simple Cocoa bindings are altering a fetch predicate attached to an ArrayController that has Auto Rearrange Content set.

My data model has three classes (we'll call them A, B, and C). It's arranged in a strict hierarchy, where an instance of class_A owns one or more instances of class_B, each of which owns one or more instances of class_C; and each instance of classes B and C has a weak reference to its parent. For example:

@interface Class_A
@property (strong) NSArray *collection_of_b;
@property BOOL foo;
@end

@interface Class_B
@property (weak) Class_A class_A;
@property (strong) NSArray *collection_of_c;
@property (weak) SomeOtherClass bar;
@end

@interface Class_C
@property (weak) Class_B class_B;
@property (weak) SomeOtherClass baz;
@end

I'm showing this hierarchy in a large master-detail view. There's a first table view with a list of all class_A instances; upon selection, a second table view presents the list of class_B instances; and upon selection again, a third table view presents the list of class_C instances.

Beneath each list, there's a set of fields bound to various properties of the selected instance of class_A, class_B, or class_C (like foo, bar, and baz). These fields either use strictly KVO-compliant accessors to modify the class members, or I'm just binding the field straight to the selection member. (For example, class_A.foo is bound to a set of radio buttons for YES and NO.) All of that seems to be working perfectly.

Now, here's the kicker. In another part of the same window, I'm presenting a filtered list of class_C instances having some properties. To present that filtered list, I'm using a filter predicate specifying some criteria like "(class_B.bar == some value) OR (class_B.class_A.foo == YES)". The filtering works perfectly... until I alter the values of foo or bar through the bound fields, when my app instantly crashes with the following message:

"Cannot remove an observer for the key path "bar" from <Class_B instance>, most likely because the value for the key "bar" has changed without an appropriate KVO notification being sent. Check the KVO-compliance of the Class_B class."

I've narrowed the problem down to the Auto Rearrange Content setting of the ArrayController. When I turn this off, the table doesn't automatically adjust the content when the filtered properties of the instances change, but it also doesn't crash. Of course, I can manually trigger the rearranging, but it's an inelegant solution.

Some searching revealed a 2009 cocoa-dev thread reporting the exact same behavior of Auto Arrange Content as a bug:

https://groups.google.com/forum/?hl=en&fromgroups#!topic/cocoa-dev/SpXF0__B4dE

Not sure what can be done about it, though. Any thoughts?

paulmelnikow
  • 16,895
  • 8
  • 63
  • 114
David Stein
  • 866
  • 7
  • 21

1 Answers1

1

I'm guessing the source of your problems are here:

@property (weak) SomeOtherClass bar;

@property (weak) Class_B class_B;

When these object are deallocated, their instance variables are set to nil by the runtime, but no KVO notification is emitted.

Try changing these to strong properties and seeing if that fixes the problem.

There's some good, but complex explanation about this situation in this blog post.

paulmelnikow
  • 16,895
  • 8
  • 63
  • 114
  • Thanks - that's an interesting assessment. I'm reluctant to use strong references to solve that problem, because it breaks the whole point of strong vs. weak: establishing a strict, hierarchical order of ownership that avoids retain cycles. I'll think on your ideas and see if I can find an intermediate solution. – David Stein Jul 17 '13 at 11:26
  • By temporarily switching them to strong you can identify specifically which one is causing the problem. At that point you can decide whether it's more important to use an auto-rearranging filter predicate or weak references. – paulmelnikow Jul 17 '13 at 13:06