Just to be sure: I have two properties, I want one of them to be set if the other changes, but not the inverse.
I have some application preferences and some document preferences. When the user opens a new document, its preferences are set to the application ones, but he’s free to change them after that. But if the application preferences are changes, the document ones are changed too (as well as future docs).
The preferences of the application will persist via the Shared User Controller, the document ones with the document.
Anyway: What I want is to add a observer (the document properties will observe the application properties). Questions:
If an application-range textfield is changed, will the doc-range textfield change? (both are bound by value to a property)
I don’t know, whether ARC removes observers automatically.
In the app delegate it’s no problem to omit it because everything is deallocated when the app quits,
but in custom classes it could cause memory leaks or at least unexpected behavior.
Personally I don’t use ARC because I want to have control about memory management.
Probably it’s just habit. I “grow up” with memory management and as iOS doesn’t support garbage collection
I’ve never used GC too
I had one problem, found its solution and just wanted to share it.
My user can choose between two way of showing some data items (both are controls in a window’s toolbar):
– a segmented control, when there are only a few items;
– a pop menu when the segmented control becomes too big for the size of the window.
I had decided to put this choice into the preferences, so he/she has not to reset the control each time. So I put a checkbox saying “Items as pop menu”. When the application is launched, it looks for this property and shows the correct control.
So far, so good.
But of course, if the user wants to change the control, the toolbar has to reflect the change in real time, not at next launch. And this became interesting.
If I set the checkbox to call an IBAction, the bound property (the preference) will not be updated. I could look at the checkbox value and set the property into the handler. Really, really ugly.
In order not to dispatch the code, I want the application to be able to call this handler-- but if the application is the sender, I can’t check it value! So I have to make an instance method of it, not an IBAction. Sad.
No way to bind the hidden flag of both controls to the checkbox value: they are in a tool bar, I don’t want the controls to appear or disappear, I want one to replace the other. No good, no good.
The solution is: using KVO.
The application launches, it tests the preference property and install the correct control into the tool bar, as before:
Now, when the user click on the check box, it does not trigger an IBAction: it just sets the bound preference value. But this value is observed (udc is the UserDefaultController):
So when the value changes, the application is aware of it and install the control, using the same routine as before:
For info, that’s what this installClassSelector method does (accessing toolbar items is made by identifiers):
And as supplementary info, the segmented control and the menu item are synchronized: the objects just say each other “Hey, my selection has changed, this is the new index, set yours”. Anytime when the user changes the control, they show the same selection.
These lines are mainly for StefanK, who pointed the KVO/KVC advantages (I wanted to solve everything using bindings, I’m really fond of them, but as rdelmar said “they’re not the answer to everything”).
I hope this code may be useful. So I won’t be just complaining about problems on this site.
To retrieve a value for a given key from a dictionary use objectForKey: rather than valueForKey:
In most cases both methods do the same, but for example you will get unexpected behavior if the key contains a dot, because valueForKey: treats the dot as keypath delimiter.
To retrieve primitive values from user defaults this could be more convenient than accessing the controller object