Re: Listening to bindings
Re: Listening to bindings
- Subject: Re: Listening to bindings
- From: Scott Stevenson <email@hidden>
- Date: Fri, 18 Feb 2005 15:26:10 -0800
On Feb 18, 2005, at 11:07 AM, Guy English wrote:
Well, first, that's a great article. Scott did a terrific job
explaining how things work and it's a really good looking page to
boot. The one critique, and I'm sure he's aware of it because it's
come up here before, is that the model objects break encapsulation.
It's true that the main idea was to minimize the amount of code and ivars, I don't believe the solution is as restrictive as it may seem on the surface.
I mentioned it being a bad idea in the original post because the
question was about tracking changes to a model. He wanted to detect
updates, inserts and deletes. The approach he was considering was
"listening to bindings" or snooping the communication so he could
update his underlying data base correctly. Clearly, unless there's
some wierd and wonderful reason for it, this isn't a good
object-oriented design.
Actually, I disagree somewhat. :)
The dictionary structure isn't the problem and the manual accessors don't solve the original problem. If you want to know which individual objects were added or removed from an array, you have to use indexed accessors:
<http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/Concepts/AccessorConventions.html>
You can either do the database conversations directly within those, or you can observe the source array. In the case of Mailbox, you would want to do the following -init or something:
<x-tad-bigger>[</x-tad-bigger><x-tad-bigger>self</x-tad-bigger><x-tad-bigger> addObserver: </x-tad-bigger><x-tad-bigger>self</x-tad-bigger><x-tad-bigger>
forKeyPath: </x-tad-bigger><x-tad-bigger>@"emails"</x-tad-bigger><x-tad-bigger>
options: (NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld)
context: </x-tad-bigger><x-tad-bigger>NULL</x-tad-bigger><x-tad-bigger>];
</x-tad-bigger>
Then implement this in Mailbox (untested):
<x-tad-bigger>- (</x-tad-bigger><x-tad-bigger>void</x-tad-bigger><x-tad-bigger>) observeValueForKeyPath: (NSString *)keyPath
ofObject: (</x-tad-bigger><x-tad-bigger>id</x-tad-bigger><x-tad-bigger>)object
change: (NSDictionary *)change
context:(</x-tad-bigger><x-tad-bigger>void</x-tad-bigger><x-tad-bigger> *)context
{
</x-tad-bigger><x-tad-bigger>id</x-tad-bigger><x-tad-bigger> item, e;
</x-tad-bigger><x-tad-bigger>id</x-tad-bigger><x-tad-bigger> theItems;
NSKeyValueChange changeKind = [[change objectForKey: NSKeyValueChangeKindKey] intValue];
</x-tad-bigger><x-tad-bigger>switch</x-tad-bigger><x-tad-bigger> (changeKind)
{
</x-tad-bigger><x-tad-bigger>case</x-tad-bigger><x-tad-bigger> NSKeyValueChangeInsertion:
theItems = [change objectForKey: NSKeyValueChangeNewKey];
e = [theItems objectEnumerator];
</x-tad-bigger><x-tad-bigger>while</x-tad-bigger><x-tad-bigger> (item = [e nextObject])
{
</x-tad-bigger><x-tad-bigger>// insert item in db</x-tad-bigger><x-tad-bigger>
}
</x-tad-bigger><x-tad-bigger>break</x-tad-bigger><x-tad-bigger>;
</x-tad-bigger><x-tad-bigger>case</x-tad-bigger><x-tad-bigger> NSKeyValueChangeRemoval:
theItems = [change objectForKey: NSKeyValueChangeOldKey];
e = [theItems objectEnumerator];
</x-tad-bigger><x-tad-bigger>while</x-tad-bigger><x-tad-bigger> (item = [e nextObject])
{
</x-tad-bigger><x-tad-bigger>// remove item from db </x-tad-bigger><x-tad-bigger>
}
</x-tad-bigger><x-tad-bigger>break</x-tad-bigger><x-tad-bigger>;
}
}
</x-tad-bigger>
Note that the KVO approach will *not* work unless you at least implement the -countOfEmails and -objectInEmailsAtIndex indexed accessors.
As for tracking the individual attribute changes ("subject", for example), you have a few options.
1. Implement the accessors as you outlined (a perfectly practical solution)
2. Subscribe for KVO notifications for "properties.subject" each time an Email is created (you can use the KVO stuff above to find out when a new Email is added to the Mailbox)
3. Make up a fake key like "anyChange" and subscribe to KVO notices for it, then make "anyChange" dependent on all other keys. This works great for a database since it's often easier to keep things in sync if you update the entire record at once, rather than just individual fields.
What I find stylistically displeasing about option #1 is that you can end up with a lot of methods that do essentially the same thing. I'd rather implement one KVO method that saves changes to the db, but that's a personal choice to some degree.
So I am curious, from an analysis standpoint, what are your reasons for
saying it's a "Bad thing"? (Not saying it isn't, but am trying to
figure it out myself).
Well I guess the strict "Bad Thing" would be that it evades
encapsulation.
Since everything goes through KVC and is dynmically bound, I don't believe this is true.
I apologize if some of the examples and explanation in this email are unclear, I was trying to keep the size of the post within reason. :)
- Scott
--
http://treehouseideas.com/
http://theocacao.com/ [blog]
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden