Binding question
-
I'm reading about binding and KVC/KVO... and I have a question for which
I can't seem to find an answer... I'll use a simple example, it'll be
easier to explain that way...
Let's say I have a simple class called "File"... my File class has a
property called filePath which is an NSString representing the path of
that file...
Now, assuming I want to bind an array of those File objects to a Table
View, however, what I would like to display in the table's column is not
the complete path of the file, but just the file name (that I get with
[filePath lastPathComponent])....
What would be the best way to achieve that? Basically: I want to bind
my column to a property that doesn't really exist (i.e. only part of the
filePath property)...
I did create a method called "- NSString *fileName" which only does this:
return [filePath lastPathComponent];
It seems to work if I bind my table's column to the
"arrangedObjects.fileName" model key but I am wondering if it is a good
idea to do it like this? Basically the "fileName" method "acts" as if it
was the getter of a "fileName" property, but that property doesn't
really exist...it just returns a part of my filePath property...
It's a bit harder to explain than I thought it would be but to sum it
up: Is it ok to bind my column to a property that is, in fact, not a
property but just a method that returns a string... or should I create
an actual instance variable "NSString *fileName" with a regular getter
and setter....? It seems like there are a lot of cases where the data I
want to show in my TableView is not an actual property of my class but a
formatted or modified version of it.. Also, keep in mind that the table
column is NOT editable so I don't really need a setter...
Any help/suggestions would be appreciated! If I'm unclear let me know
I'll try to explain it better!
Jean-Nicolas Jolivet -
On Oct 28, 2008, at 12:19 AM, Jean-Nicolas Jolivet wrote:> Now, assuming I want to bind an array of those File objects to a
> Table View, however, what I would like to display in the table's
> column is not the complete path of the file, but just the file name
> (that I get with [filePath lastPathComponent])....
>
> What would be the best way to achieve that? Basically: I want to
> bind my column to a property that doesn't really exist (i.e. only
> part of the filePath property)...
You can use a custom value transformer. You would implement
+transformedValueClass to return an [NSString class] and -
transformedValue: to return the lastPathComponent of the passed in
string. Set the transformer for the binding and you should be set.
There are a few samples that you can reach from the Xcode docs
demonstrating this (just look up NSValueTransformer).
--
David Duncan
Apple DTS Animation and Printing -
On Oct 28, 2008, at 00:19, Jean-Nicolas Jolivet wrote:> Is it ok to bind my column to a property that is, in fact, not a
> property but just a method that returns a string... or should I
> create an actual instance variable "NSString *fileName" with a
> regular getter and setter....?
A property *is* just a method (or, if readwrite, a pair of methods --
the getter and the setter). The instance variable, if there is one, is
"merely" an implementation detail within the class. Some properties
don't use an instance variable (NSString's lastPathComponent property
almost certainly doesn't, for example). Some properties (like your
"fileName") use an instance variable, but compute a value from it.
Furthermore, assuming that your File class has both filePath and
fileName properties, then your fileName implementation:
return [filePath lastPathComponent];
might alternatively be:
return [self.filePath lastPathComponent];
If you get the difference, you're home free, conceptually. (The 2nd
one extracts the file name from your filePath property, without any
assumption about how the property is implemented. The 1st one uses a
convenient instance variable that happens to contain the information
you want. Either approach is fine in this case, but in more
complicated cases, it's important to distinguish between the value of
the property and the value of some variable.)
Finally, you also need to pay attention to KVO compliance. Assuming
your filePath property is compliant (meaning that changes to it
produce the proper KVO notifications), your fileName property isn't
(unless the filePath never changes in a File object after it is
initialized, in which case the question is moot).
HTH -
Thanks a lot, this was extremely helpful!
One more thing, you mentioned the term "KVO Compliance"... I understand
that this means to send the proper notifications when a change to my
property has been made...
But: would it be considered bad practice if my file class does have a
property that is used just for displaying purpose (For example, by
combining other properties (fileName, extension, size) into a nicely
formatted string) to display in a tableView... by bad practice I mean: I
know that this property is only used for display purpose and will never
be edited... but will be modified when other properties (filePath etc..)
have been modified?
Would it mean that this property is not "KVO compliant" and if so, is it
a problem? Assuming I know it should never be "observed" (i.e. it will
never be modified directly, only by modifying other properties)...
basically from your post I understand that.. technically, it's not a
problem to do it like that, but... would it be considered bad practice
since the property is not KVO compliant? and if so, is it even possible
to make it KVO compliant?
Jean-Nicolas Jolivet
Quincey Morris wrote:> On Oct 28, 2008, at 00:19, Jean-Nicolas Jolivet wrote:
>
>> Is it ok to bind my column to a property that is, in fact, not a
>> property but just a method that returns a string... or should I
>> create an actual instance variable "NSString *fileName" with a
>> regular getter and setter....?
>
> A property *is* just a method (or, if readwrite, a pair of methods --
> the getter and the setter). The instance variable, if there is one, is
> "merely" an implementation detail within the class. Some properties
> don't use an instance variable (NSString's lastPathComponent property
> almost certainly doesn't, for example). Some properties (like your
> "fileName") use an instance variable, but compute a value from it.
>
> Furthermore, assuming that your File class has both filePath and
> fileName properties, then your fileName implementation:
>
> return [filePath lastPathComponent];
>
> might alternatively be:
>
> return [self.filePath lastPathComponent];
>
> If you get the difference, you're home free, conceptually. (The 2nd
> one extracts the file name from your filePath property, without any
> assumption about how the property is implemented. The 1st one uses a
> convenient instance variable that happens to contain the information
> you want. Either approach is fine in this case, but in more
> complicated cases, it's important to distinguish between the value of
> the property and the value of some variable.)
>
> Finally, you also need to pay attention to KVO compliance. Assuming
> your filePath property is compliant (meaning that changes to it
> produce the proper KVO notifications), your fileName property isn't
> (unless the filePath never changes in a File object after it is
> initialized, in which case the question is moot).
>
> HTH
> -
Mmmm seems like value transformer would be another way to do it! I will
definitely look into both methods! Since my property is in fact only
used for display purposes, I guess it would make sense to use a
transformer to display it...
Jean-Nicolas Jolivet
David Duncan wrote:> On Oct 28, 2008, at 12:19 AM, Jean-Nicolas Jolivet wrote:
>
>> Now, assuming I want to bind an array of those File objects to a
>> Table View, however, what I would like to display in the table's
>> column is not the complete path of the file, but just the file name
>> (that I get with [filePath lastPathComponent])....
>>
>> What would be the best way to achieve that? Basically: I want to
>> bind my column to a property that doesn't really exist (i.e. only
>> part of the filePath property)...
>
>
> You can use a custom value transformer. You would implement
> +transformedValueClass to return an [NSString class] and
> -transformedValue: to return the lastPathComponent of the passed in
> string. Set the transformer for the binding and you should be set.
>
> There are a few samples that you can reach from the Xcode docs
> demonstrating this (just look up NSValueTransformer).
> --
> David Duncan
> Apple DTS Animation and Printing
>
> -
On Oct 28, 2008, at 12:30, Jean-Nicolas Jolivet wrote:> One more thing, you mentioned the term "KVO Compliance"... I
> understand that this means to send the proper notifications when a
> change to my property has been made...
>
> But: would it be considered bad practice if my file class does have
> a property that is used just for displaying purpose (For example, by
> combining other properties (fileName, extension, size) into a nicely
> formatted string) to display in a tableView... by bad practice I
> mean: I know that this property is only used for display purpose and
> will never be edited... but will be modified when other properties
> (filePath etc..) have been modified?
No, it's not bad practice to have a "derived" property for display
purposes. That's basically what -[NSObject description] is, for
example, and that's a *really* important property.> Would it mean that this property is not "KVO compliant" and if so,
> is it a problem? Assuming I know it should never be "observed" (i.e.
> it will never be modified directly, only by modifying other
> properties)...
Not sure what this means. "Observed" doesn't mean modifiable. It means
some other object is watching for change notifications. In particular,
if you bind something to your derived property (which is what you're
doing in your table view), the property gets observed by the binding,
and so must be KVO compliant.> basically from your post I understand that.. technically, it's not a
> problem to do it like that, but... would it be considered bad
> practice since the property is not KVO compliant? and if so, is it
> even possible to make it KVO compliant?
There's at least 2 ways. If you have a setter for "filePath", you can
do this:
- (void) setFilePath: (NSString*) newValue {
[self willChangeValueForKey: @"fileName"];
filePath = newValue; // plus retain/release code too, if you aren't
using garbage collection
[self didChangeValueForKey: @"fileName"];
}
Or you can have it happen automatically:
@implementation File
+ (NSSet*) keyPathsForValuesAffectingFileName {
return [NSSet set withObject: @"filePath"];
}
...
(The latter is the Leopard way. For Tiger, you use
'setKeys:triggerChangeNotificationsForDependentKey:' instead.) -
Well, I think that pretty much answers all my questions! Thanks a lot
for the detailed explanation! I understand the KVC/KVO principle much
better now! :)
Jean-Nicolas Jolivet
Quincey Morris wrote:> On Oct 28, 2008, at 12:30, Jean-Nicolas Jolivet wrote:
>
>> One more thing, you mentioned the term "KVO Compliance"... I
>> understand that this means to send the proper notifications when a
>> change to my property has been made...
>>
>> But: would it be considered bad practice if my file class does have a
>> property that is used just for displaying purpose (For example, by
>> combining other properties (fileName, extension, size) into a nicely
>> formatted string) to display in a tableView... by bad practice I
>> mean: I know that this property is only used for display purpose and
>> will never be edited... but will be modified when other properties
>> (filePath etc..) have been modified?
>
> No, it's not bad practice to have a "derived" property for display
> purposes. That's basically what -[NSObject description] is, for
> example, and that's a *really* important property.
>
>> Would it mean that this property is not "KVO compliant" and if so, is
>> it a problem? Assuming I know it should never be "observed" (i.e. it
>> will never be modified directly, only by modifying other properties)...
>
> Not sure what this means. "Observed" doesn't mean modifiable. It means
> some other object is watching for change notifications. In particular,
> if you bind something to your derived property (which is what you're
> doing in your table view), the property gets observed by the binding,
> and so must be KVO compliant.
>
>> basically from your post I understand that.. technically, it's not a
>> problem to do it like that, but... would it be considered bad
>> practice since the property is not KVO compliant? and if so, is it
>> even possible to make it KVO compliant?
>
> There's at least 2 ways. If you have a setter for "filePath", you can
> do this:
>
> - (void) setFilePath: (NSString*) newValue {
> [self willChangeValueForKey: @"fileName"];
> filePath = newValue; // plus retain/release code too, if you
> aren't using garbage collection
> [self didChangeValueForKey: @"fileName"];
> }
>
> Or you can have it happen automatically:
>
> @implementation File
> + (NSSet*) keyPathsForValuesAffectingFileName {
> return [NSSet set withObject: @"filePath"];
> }
> ...
>
> (The latter is the Leopard way. For Tiger, you use
> 'setKeys:triggerChangeNotificationsForDependentKey:' instead.)
> -
On Oct 28, 2008, at 12:58, Quincey Morris wrote:> return [NSSet set withObject: @"filePath"];
Er, I meant:> return [NSSet setWithObject: @"filePath"];
Also, I think it's worth adding that it's also not bad practice to
have properties that are not KVO-compliant (that is, properties that
don't generate the proper KVO notifications when their value changes).
If you're not going to observe them (e.g. aren't going to bind
anything to them), there's no need to write code to make them
observable.


