Re: bindings duplicates object returned
Re: bindings duplicates object returned
- Subject: Re: bindings duplicates object returned
- From: Eric Slosser <email@hidden>
- Date: Tue, 13 Dec 2011 13:04:50 -0500
I found the solution, and it's all my fault.
The object 'foo' was meant to be a singleton instance of class 'Foo'. I always access it through [foo instance], which enforces that. After 'foo' is created, the nib is loaded in which the binding was expressed. In the process (see stack below**), -[Foo init] is called. My override of -[Foo init] didn't have singleton smarts, so I happily handed a different m_contents to the controller.
So I thought that bindings had duplicated the result of -(id)[foo contents], but actually, I had two different 'foo's.
Truly a foo-lish mistake.
** le stack
-[Foo init]
-[NSCustomObject nibInstantiate]
-[NSIBObjectData instantiateObject:]
-[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:]
loadNib
On Dec 13, 2011, at 12:11 PM, Eric Slosser wrote:
> Thanks for the pointers, Quincy. However, I implemented the following accessors, checked I wasn't directly touching m_contents except in my init, and the problem persists.
>
> - (void) setContents:(id)newValue
> - (NSUInteger)countOfContents
> - (id)objectInContentsAtIndex:(NSUInteger)idx
> - (void)insertObject:(id)obj inContentsAtIndex:(NSUInteger)idx
> - (void)removeObjectFromContentsAtIndex:(NSUInteger)idx
>
> I implemented -(BOOL)[Foo respondsToSelector:] to see what else I might be missing, but Foo is only asked about '_finishedMakingConnections' and '_autounbinder'. If I'm still missing an accessor, it must be determined without using respondsToSelector:.
>
> On Dec 8, 2011, at 12:34 AM, Quincey Morris wrote:
>
>> On Dec 7, 2011, at 20:05 , Eric Slosser wrote:
>>
>>> I have an NSTreeController whose 'content array' is bound to another object's 'content' key.
>>>
>>> It appears that the binding mechanism duplicates the array that was returned by [foo content].
>>>
>>> This is bad, because changes that 'foo' makes later to its m_content never show up in the window. It's also bad because [NSTreeController selectedObject] doesn't correspond to anything in the tree under [foo content].
>>>
>>> I can work around this by NOT using bindings to establish the controller's content, instead using [NSTreeController setContent:].
>>>
>>> 1. Where in the docs is this binding duplication described?
>>> 2. Is there a way to use bindings and avoid having [foo content] duplicated?
>>
>> Based on this information, the problem is that your Foo class isn't KVO compliant for the "content" property.
>>
>> Conceptually, it's a mistake to think of "content" as an array property (that is, as a property that has an array as its value). Conceptually, you should think of it as an indexed to-many relationship.
>>
>> The correct approach is to properly implement the KVC indexed collection accessors in your Foo class**. See:
>>
>> http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueCoding/Articles/AccessorConventions.html#//apple_ref/doc/uid/20002174-BAJEAIEE
>>
>> Normally, you need to write only the 'insert' and 'remove' accessors. You can let all of the others default. Then, when changing the value of the property, you should never change m_content directly***. Instead, use the collection accessors, or use the mutable array proxy. For example, to add an object to the array, use either of these:
>>
>> [foo insertObject: newObject inContentAtIndex: newIndex]; // or …
>> [[foo mutableArrayValueForKey: @"content"] insertObject: newObject atIndex];
>>
>> Both of these are KVO compliant, and functionally equivalent.
>>
>> Once you have this machinery set up, your binding to Foo's "content" should work properly.
>>
>>
>>
>> ** Of course, in practice, the Foo instance has to return *some* object that looks like an array as the value of the "content" getter, since properties have types. But think about what object ought to be returned. You don't want to return your 'm_content' array, because you're exposing an implementation detail to clients of your Foo class. You don't want to return a copy of your 'm_content' array, because that of course won't change after being created.
>>
>> Catch-22, isn't it? Ideally, the Foo instance would return [self immutableArrayValueForKey: @"content"] but unfortunately there's no such method. The simplest choice is probably to return the ivar, but to write no code that uses the @property value.
>>
>> *** When using an array ivar to back an indexed collection property like this, it's safe to modify the ivar directly in the init method only (because there aren't any observers at this point). Apart from that method, you should always use the KVO compliant accessors.
>>
>
> _______________________________________________
>
> 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
>
>
_______________________________________________
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