Why would you bind the value of a NSProgressIndicator? - cocoa

What's the point of binding the value of a NSProgressIndicator to your controller? It never seems to ask the controller for the value, except on startup. The only way to move the NSProgressIndicator seems to be by sending it #increaseBy:, which bypasses my binding. So, why would I bind?!

If your UI's bound value not updating, that means you either bungled the binding or your controller code is not modifying the bound value in a key-value-observing–compliant way. The most common problem is doing fooIvar = val rather than [self setFooIvar:val] or self.fooIvar = val.
Apple's answer to your problem:
[What to do if] Changing the value of a model property programmatically is not reflected in the user interface
If changes made to a model value programmatically are not being reflected in the user interface, this typically indicates that the model object is not key-value-observing compliant for the property, or that you are modifying the value in a manner that is bypassing key-value observing. You should ensure that:
The model class has automatic key-value observing enabled or implements manual key-value observing for the property.
That you are changing the value using an accessor method, or using a key-value-coding compliant method. Changing the value of an instance variable directly does not provide key-value observing change notifications.
If your model property is a collection, that you're modifying the content in a key-value-observing compliant manner. See “My collection controller isn’t displaying the current data” for more information.
For that answer and answers other common problems, see "Troubleshooting Cocoa Bindings."
You should also look at the examples provided by mmalc. They are a valuable resource.

Related

Core Data: "-add<RelationshipKey>Object:" not called

Let's presume I have an entity BikeRider with a relationship property called helmets.
I have an array controller bound to the app's managed object context, with entity set to BikeRider. There's a tableview that lists all bike riders.
Then, I have a second array controller, bound to the app's managed object context, with entity set to Helmet. Additionally, it's bound to bikeRiderArrayController.selection. There's a second tableview that lists all helmets for the selected bike rider.
I also have two buttons for adding and removing helmets. The setup works apparently flawlessly.
Except, of course for one small thing: it looks like -addHelmetsObject:, -removeHelmetsObject:, -addHelmets: and -removeHelmets: never get called. This means some code for setting up observation of each helmet's color property never gets called.
What am I missing? Isn't overriding addHelmets: et al (with proper willChangeValueForKey: et al notifications) the right way to get notified of additions?
Do I really have to [self observeValueForKey:#"helmets". . .] and then [oldValue minusSet:newValue] and vice versa to figure out which objects were added or removed? I could swear the methods were being correctly called in the past. Maybe some key element of the setup is now different.
This has never worked properly through NSArrayController. From Apple's docs:
Custom relationship set mutator methods are not invoked by an arraycontroller
Problem: You have implemented set mutator methods for a relationship as described in “Custom To-Many Relationship Accessor
Methods,” and have bound the contentSet binding of an
NSArrayController instance to a relationship, but the set mutator
methods are not invoked when you add objects to and remove objects
from the array controller.
Cause: This is a bug.
Remedy: You can work around this by adding self to the contentSet binding's key path. For example, instead of binding to [Department
Object Controller].selection.employees, you would bind to [Department
Object Controller].selection.self.employees.

Is there a reason why the default modelbinder doesn't bind to fields?

I'm using ASP.NET MVC3 and i'm wondering that the default modelbinder binds to public properties but not to public fields.
Normally i just define the model classes with properties but sometimes i use some predefined classes which contains some fields. And everytime i have to debug and remember that the modelbinder just don't like fields.
The question: Whats the reason behind it?
but sometimes i use some predefined classes which contains some fields
While I cannot answer your question about the exact reason why the default model binder works only with properties (my guess is that it respects better encapsulation this way and avoids modifying internal state of the object which is what fields represent) I can say that what you call predefined classes should normally be view models. You should always use view models to and from your controller actions. Those view models are classes that are specifically defined to meet the requirements of the given view.
So back to the main point: fields are supposed to be modified only from within the given class. They should not be accessed directly from the outside. They represent and hold internal state of the class. Properties on the other hand is what should be exposed to the outside world. Imagine that in the property getter/setter you had some custom logic. By modifying directly the field this custom logic would be broken and potentially bring the object into an inconsistent state.
Maybe the reason for ignoring fields is to increase performance of the binder. Instead of searching all the Fields and properties. The Model Binder search for Properties only.
Though I think the Model Binder use cache to improve performance.
DefaultModelBinder exposes a public method:
DefaultModelBinder.BindModel, and a number of protected method available for overriding. All of them listed here.
Besides the model, these method refer to properties only, not fields, like
GetModelProperties,
GetFilteredModelProperties,
GetPropertyValue,
OnXYZValidating,
OnXYZValidated,
OnXYZUpdating,
OnXYZUpdated,
GetXYZValue,
where XYZ stands for either Model, or Property/ies, or both, and so on.
As you can see there is no Fields mentioned with these names whatsoever. As Darin explained no direct changes to Model's state are tolerated by the Binder. Hence no Field in its methods.
And also, you may wish to take a look at another important class: ModelBindingContext. An instance of this class gets passed to the BindModel, and subsequently to BindSimpleModel, and BindComplexModel, depending on model type (string, int,... are considered simple, everything else is complex).
So, this context has the following properties:
ModelXYZ, and
PropertyXYZ.
In other words you have no means to reference the fields in your ViewModel unless you do not override these classes and undertake special actions to do so.
But again, beware of fighting the framework, its always easier to follow it instead.
EDIT: The ModelMetadata class holds all the data needed to bind the model. Its code however, shows no sign of fields, field names, etc. Only properties are referenced and accessed. So, even if you try to inherit and override DefaultModelBinder and ModelBinderContext, you still won't be able to access fiellds, nevermind what their access modifier is: public, private, etc.
Hope this explains most of it.

NSTreeController fetch predicate based on transient isRoot exceptions

My document-based Cocoa application uses a NSOutlineView/NSTreeController combo, bound to the document's Core Data store. My NSTreeController has the fetch predicate isRoot == YES. isRoot is a transient boolean attribute with a default value of NO. My root model's awakeFromInsert calls:
[self setIsRoot:[NSNumber numberWithBool:YES]];
I'm able to add objects to the hierarchy just fine, but when I try to load a document I just saved, I get an exception:
[<NSDictionaryMapNode 0x1001a8190> valueForUndefinedKey:]: this class is not key value coding-compliant for the key isRoot.
I can work around this exception and successfully load a newly-saved document if I change the isRoot attribute to non-transient in the xcdatamodel, but based on my understanding of the transient flag it should not cause a problem, and this really isn't the kind of data that should be persisted.
I have also tried implementing -isRoot in the NSManagedObject subclasses to return the appropriate fixed value, as well as making the same setIsRoot: call within awakeFromFetch, both to no avail.
Is there some other subtlety I'm missing? I can't imagine that fetch predicates don't support transient attributes. I don't know much about the inner workings of Core Data but it seems interesting that it's trying to look up isRoot on the store-specific class and not my NSManagedObject subclass.
I can't imagine that fetch predicates
don't support transient attributes.
After a bit of research, I can tell you that they don't. See this document. Quote:
You cannot fetch using a predicate
based on transient properties
(although you can use transient
properties to filter in memory
yourself).
I've put together a test project and can verify I get exactly the same error as you do.
When I need to filter out the root nodes in a tree, I use a fetch predicate of parent == nil instead of a transient attribute.
I understand your reaction - I too wanted way of having an attribute specifically called isRoot too. My guess is it's possible, but it'd take so much code it's just not worth the hassle.
Oh, and if you're dealing with core data any more than a little, mogenerator will make your life much easier.
Another option is to have a separate class for the top-level nodes, use that class name as "Entity Name" and leave "Fetch Predicate" blank. As long as the child nodes have the same values as the top-level node (I use a common superclass/entity inheritance), everything still works.
Have you made sure that the NSTreeController is set to control an entity rather than a class?
From your error, it looks like it might be set to a class with the default - NSMutableDictionary.
I'd also argue that maybe isRoot could be persisted. It depends on what you're trying to do with your app, of course, but if it's a tree view that gets loaded on app run I'd either make isRoot persist.

Cocoa bindings: custom setter methods?

I'm using Cocoa bindings to manage a table of objects. I understand how bindings work but I've run into a slight problem. Managing the table of objects would be fine and dandy, except that those objects have to manage actual bluetooth hardware. I'm working off of a framework that provides a class representing a connection to this hardware, and have made another "manager" class the makes it key-value compliant. In other words, this manager class has to be able to connect and modify its "connect" status in its properties dictionary, be the delegate of this hardware and modify properties, and update the hardware with changes made.
However, whenever I set new values within the object itself, like in a "connect" method that would change the "connect" key's value to 2 (looking), (i.e. propertiesDict = newDict), the change is not seeming to be picked up by observers that it is bound to. I've looked at the observeValueForKeyPath:ofObject:change:context: in the NSKeyValueObservingProtocol. However, I don't know what to do with the context argument.
I hope that makes sense... but if anyone has any ideas I'd love to hear them.
Your question isn't totally clear, but if I'm understanding it correctly the issue might be because you need to send manual KVO notifications before and after you change a value in the embedded object. For instance, [self willChangeValueForKey:#"connected"]; and [self didChangeValueForKey:#"connected"];.
There are three ways to update a property/attribute in a KVO compatible way:
Using the property setter (specified in #property declaration or generated by #synthesize)
Calling -willChangeValueForKey: and -didChangeValueForKey: before and after you change the property value in any way.
Calling -setValueForKey:

observeValueForKeyPath:ofObject:change:context: doesn't work properly with arrays

I have an object that implements the indexed accessor methods for a key called contents. In those accessors, I call willChange:valuesAtIndexes:forKey: and didChange:valuesAtIndexes:forKey: when I modify the underlying array.
I also have a custom view object that is bound to contents via an NSArrayController. In observeValueForKeyPath:ofObject:change:context: the only value in the change dictionary for the NSKeyValueChangeKindKey I ever see is NSKeyValueChangeSetting. When I'm adding objects to the array, I expect to see NSKeyValueChangeInsertion.
Recreating my view's internal representation of the objects it observes every time I insert a single item -- particularly when I'm bulk loading hundreds of items -- presents quite a performance problem, as you'd imagine. What am I doing wrong that Cocoa seems to think I'm setting a completely new array each time I add or remove a single item?
(Note to all readers: I hate using answers for this, too, but this discussion is too long for comments. The downside, of course, is that it ends up not sorted chronologically. If you don't like it, I suggest you complain to the Stack Overflow admins about comments being length-limited and plain-text-only.)
I don't understand what you mean by implementing array accessors in the view.
Implement accessors, including indexed accessors, for the mutable array property that you've exposed as a binding.
Bindings is built on top of KVO.
And KVC.
All bindings are implemented using observeValueForKeyPath:
Overriding that is one way, sure. The other way is to implement accessors in the object with the bindable property (the view).
My custom view provides a binding that the app binds to an array -- or in this case, an array controller. Accessor methods apply to KVC, not KVO.
Cocoa Bindings will call your view's accessors for you (presumably using KVC). You don't need to implement the KVO observe method (unless, of course, you're using KVO directly).
I know this because I've done it that way. See PRHGradientView in CPU Usage.
Curiously, the documentation doesn't mention this. I'm going to file a documentation bug about it—either I'm doing something fragile or they forgot to mention this very nice feature in the docs.
It absolutely matters that I'm getting a set message on every array update. I wouldn't have posted it as a question if it didn't matter.
There are quite a large number of people who engage in something called “premature optimization”. I have no way of knowing who is one of them without asking.
I have an object that implements the indexed accessor methods for a key called contents. In those accessors, I call willChange:valuesAtIndexes:forKey: and didChange:valuesAtIndexes:forKey: when I modify the underlying array.
Don't do that. KVO posts the notifications for you when you receive a message to one of those accessors.
I also have a custom view object that is bound to contents via an NSArrayController. In observeValueForKeyPath:ofObject:change:context: the only value in the change dictionary for the NSKeyValueChangeKindKey I ever see is NSKeyValueChangeSetting. When I'm adding objects to the array, I expect to see NSKeyValueChangeInsertion.
For one thing, why are you using KVO directly? Use bind:toObject:withKeyPath:options: to bind the view's property to the array controller's arrangedObjects (I assume) property, and implement array accessors (including indexed accessors, if you like) in the view.
For another, remember that arrangedObjects is a derived property. The array controller will filter and sort its content array; the result is arrangedObjects. You could argue that permuting the indexes from the original insertion into a new insertion would be a more accurate translation of the first change into the second, but setting the entire arrangedObjects array was probably simpler to implement (something like [self _setArrangedObjects:[[newArray filteredArrayUsingPredicate:self.filterPredicate] sortedArrayUsingDescriptors:self.sortDescriptors]]).
Does it really matter? Have you profiled and found that your app is slow with wholesale array replacement?
If so, you may need to bind the view directly to the array's content property or to the original array on the underlying object, and suffer the loss of free filtering and sorting.
I call the KVO methods manually for reasons outside the scope of this issue. I have disabled automatic observing for this property. I know what I'm doing there.
I don't understand what you mean by implementing array accessors in the view. Bindings is built on top of KVO. All bindings are implemented using observeValueForKeyPath: My custom view provides a binding that the app binds to an array -- or in this case, an array controller. Accessor methods apply to KVC, not KVO.
It absolutely matters that I'm getting a set message on every array update. I wouldn't have posted it as a question if it didn't matter. I call something like
[[modelObject mutableArrayValueForKey:#"contents"] addObjectsFromArray:hundredsOfObjects];
On every insertion, my view observes a whole new array. Since I'm potentially adding hundreds of objects, that's O(N^2) when it should to be O(N). That is completely unacceptable, performance issues aside. But, since you mention it, the view does have to do a fair amount of work to observe a whole new array, which significantly slows down the program.
I can't give up using an array controller because I need the filtering and sorting, and because there's an NSTableView bound to the same controller. I rely on it to keep the sorting and selections in sync.
I solved my problem with a complete hack. I wrote a separate method that calls the KVO methods manually so that only one KVO message is sent. It's a hack, I don't like it, and it still makes my view reread the entire array -- although only once, now -- but it works for now until I figure out a better solution.

Resources