On Sep 11, 2015, at 15:49 , Alex Hall <mehg...@icloud.com> wrote:
> 
> I followed all of that, and I see what you're saying. The binding to an array 
> is, to it, no different than binding to a boolean or a string, it's just a 
> single object. But how does a binding know what it's accessing? I figured it 
> was down to the type of object being bound to; bind to an array, and you've 
> got a 1-to-many, bind to, say, an integer, and you've got a 1-to-1. It sounds 
> like it's more in what methods the view controller (in my case) implements.

It’s all about context. Each UI element (KVC “client”) knows what kind of 
relationship it’s expecting. A text field needs a scalar value, so it expects a 
one-to-one relationship to be named by the property. A table view needs row 
data, so it expects an indexed one-to-many relationship.

When a client asks for information via KVC, the message that gets sent may or 
may not be specific to a particular kind of relationship. So you actually have 
to start breaking things down into cases.

For a text field that wants to retrieve a model value (a scalar), it uses the 
KVC method ‘valueForKey:’ (or possibly ‘valueForKeyPath:’, which is a 
convenience method in KVC that breaks a key path into individual keys, and 
invokes ‘valueForKey:’ sequentially). KVC figures out how to retrieve the 
value, and does things like transforming a numeric property value into a 
NSNumber object, since everything has to be an object for KVC.

For a table view that wants to retrieve (say) the tweet for row 20 of the 
table, it uses the KVC method … surprise! … ‘valueForKey:’, with “tweets” as 
the key. The object that KVC returns is — conceptually — an array *proxy* 
object. That is, it’s an Apple-provided object that responds to all of the 
NSArray behavior, but knows how to translate each NSArray method into an access 
to the real array that’s backing the property — or the various custom accessors 
that provide values, if  there’s no real array.

So the table view ends up with a NSArray-like object, and it can use 
‘objectAtIndex: 19’ to find the object in the 20th row. The reason there’s a 
proxy involved here is to allow the model class to avoid *constructing* an 
array to return (a potentially expensive operation), while letting the client 
use the full power of the full NSArray API. That’s why, if the table view wants 
to know the number of rows, it’s going to send a ‘count’ message rather than a 
‘countOfTweets’ method.

In short, KVC has leveraged the existing complexity of the NSArray class API to 
provide a rich choice of ways to access indexed one-to-many property values, 
without having to re-invent an entirely new set of analogous API methods.

Things are a little more complicated for changing values in the model instead 
of retrieving them. For scalars — that is, in contexts where the client needs a 
one-to-one property, the KVC method it calls is ‘setValue:forKey:’. This 
usually gets translated into a 'setXxx:’ method invocation, the accessor you 
expect. It’s worth noting that if this setter is the *only* way that the 
property way can be changed, then you get automatic KVO compliance for the  
property, which is a Good Thing™.

If a table view needs to modify (say) the tweet for row 20, it does *not* use 
‘setValue:forKey:’. Functionally, it *could* do that, and KVC would do the 
right thing, but that would require the table view to construct an entire array 
of tweets, which is horribly expensive. Instead, it would really like to use 
something like a proxy object, so that the elements that are unchanged don’t 
cost anything to keep unchanged. This is exactly what happens. The table view 
calls ‘mutableArrayValueForKey:’ to get what’s called a “mutable proxy”. As 
retrieved, it represents the current model values using the NSMutableArray API, 
so it allows the table view to use the ‘replaceObjectAtIndex:withObject:’ on 
the proxy to make just that simple update.

The other cool thing that the mutable proxy does is modify the data model in a 
KVO compliant way. It does this by translating the NSArray API method into a 
series of invocations of your custom accessor methods (such as the insert… and 
remove… methods). As with one-to-one properties, if you always modify a 
one-to-many property via the mutable proxy, then the property is automatically 
KVO compliant.

It should be clear, if you think about this, why you want to avoid using 
NSArray-valued one-to-one properties. KVC and its clients can’t tell the 
difference between this (the value is an object which happens to an NSArray 
subclass) and one-to-many properties (the values are the contents of the proxy 
objects, not the proxy objects themselves). Doing the wrong thing with the 
object as value vs. the object as temporary packaging is going to wreck your 
application logic pretty fast.

Now, for one-to-many properties, there’s another wrinkle. When getting values, 
since the proxy is read-only, if you have an actual array as backing store, 
then the actual array works perfectly well as its own KVC “proxy". Only in 
special cases do you need an actual proxy for read-only purposes, and an actual 
proxy is awkward because the NSArray proxy object that KVC is designed to 
provide is more or less impossible to get hold of in a Cocoa app written to 
modern code conventions.

But there’s more, but this time I’ll switch to a second post before I get 
moderated instead of after.

(continued in part 2)



_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to