4
votes

Does anyone know how to prevent Cocoa binding to set values back to the bound property but still fires the default action selector?

I am trying to bind a NSTextField to a readonly NSString property on my data object. My data Object is like

...

@property(readonly) NSString* outCome;

-(void)otherMethodsAffectOutCome;

...

I bound the NSTextField with the outcome property and in default action handler I called -otherMethodAffectOutCome, and I hope will/didChangeValueForKey to fire outCome property's observer and back the NSTextField.

But it doesn't work, NSTextField will crash because it tries to set changed text back via setOutCome method... I think I need NSTextField watch the property value changes, but don't try to set values back on text change, How should I do that?

Thanks! -Jonny

2

2 Answers

2
votes

Since you have your own model object already, there are two options better than overriding -setValue:forUndefinedKey:.

  1. Simply change the property not to be readonly. Since it's an Objective-C object, it should be retained.

  2. If you need to have the property exposed as readonly to outside world, you can either manually define setOutCome: solely in the implementation; to the outside world it'll seem like the property is read-only, unless some wisehead comes along and directly calls -setOutCome:. One such wisehead is Cocoa Bindings technology itself.

    This is a bad idea unless you know very well how a retain setter should work and if you're very careful about it. Also, you'll have to turn the property into nonatomic -- the suggested setting on iOS, but not on OS X.

  3. Finally, here is the best solution of these three, a variant of method 2. Aside from "categories", classes can also have "extensions". Simply explained, you should think of these as not-really-categories: additional declarations of methods declared inside your main implementation file (inside the .m file) and implemented inside your main implementation file. This is used to define additional private interface for your class.

    In code, it seems as if it's a category @interface, except parentheses are empty, omitting the category name. Also, it's a weird category in that it doesn't have a matching @implementation.

    How is this useful for you?

    You can define the property in your header as (retain, readonly), but then redefine it as (retain) in the class extension. @synthesize will now create both getter and setter accessors.

So let's take a look at an example of method 3.

#import <Foundation.h>

@interface MyClass : NSObject
{
    id _myValue;
}
@property (retain, readonly) id myValue;
@end

//
#import "MyClass.h"

@interface MyClass ()
@property (retain) id myValue;
@end

@implementation MyClass
@synthesize myValue = _myValue;
@end

Now the fact that the property actually contains a setter is your implementation detail, while at the same time you didn't write your own setter. It's possibly slightly faster that it'd be if you wrote it yourself, and you didn't have to be careful about the order of retain/release calls and of the assignment operations.

0
votes

I came up with a workaround finally by overriding the setValue:forUndefinedKey: method on my data object. if the key name is outCome, just return without calling super.