Mon 03 Mar 2008 10:50:59 PM UTC, comment #8:
Great, my example works now perfectly.
|
Mon 03 Mar 2008 12:18:11 PM UTC, comment #7:
Once I looked deep enough I spotted quite a few problems in the KVO code (as well as missing features), so in the end I rewrote the data structures and code for handling observations/notifications. I hope this will fix everything, but as it's quite a major change it probably needs quite a bit of testing.
|
Sun 02 Mar 2008 12:30:07 PM UTC, comment #6:
Your change reduced the amount of get calls from three to two. What happens shouldn't be too hard to understand. Instead of calling three overriden methods the code now calls two, setValue:forKey and the real setter method.
As we have two calls for the same key in one call stack, the behaviour that I diagnozed in my last entry still persists. The overriden code in setVale:forKey: sets up a change for this key, next it calls the original method, this calls the overriden setter, here again a change gets set up for the key (overriding the previous change for the same key). Then this method calls the original setter, next it gets the new value and sends the change notification. Finally it resets the change info to nil. The stack gets unrolled up to the overriden setValue:forKey:, here we try to send another change notification, but now the change info is nil, so all the values we get here are lost and nil is send as change.
What is a bit harder to say is, what the actual expected behaviour should look like. We should of course only send one change notification, but should that come from the overriden setter or from the overriden setValue:forKey method? I think Apple only uses the setter, but doing it in the setValue:forKey: method could be easier to implement. We would just check, whether there is already a change info stored for the current key and not process any notification stuff. That works nicely with any amount of overriden methods on the call stack.
|
Sun 02 Mar 2008 09:24:30 AM UTC, comment #5:
You know I was having trouble at FOSDEM understanding the complicated sequence of methods that things were going through in the KVO code ... but couldn't really tell what I thought was wrong?
Well, I've figured it out ... the takeValue... stuff really shouldn't have been being used.
The KVC code is set up to call trhe obsolete/deprecated takeValue... methods from within the setValue... methods if the takeValue... methods have been overridden and are no longer the default NSObject ones. The idea is that, if a programmer has overridden the old methods then they presumably want their implementations to be used.
The problem arises when the KVO code replaces the standard methods in order to monitor setting of ivars/properties. The KVC code sees the overridden methods and assumes that it should call them. I've changed the KVC code to recognise the case where the KVO code has overriden the deprecated methods, and not call them.
However, while this prevents the multiple calls to notify the observer when the value is set, it does not avoid the multiple calls to get the value ... leading me to suspect that our KVO implementation behavior is significantly different from that of Apple and we need to investigate and bring the behaviors more in line.
|
Sun 02 Mar 2008 05:17:43 AM UTC, comment #4:
The diagnosis may well be correct, but the I simply can't be sure from looking at the example program.
The problem is that the test program is not the simple testcase for KVO I was looking for, but instead implicitly includes all the gui bindings stuff ... so without a clear understanding of bindings (and a surety that they are implemented the same way in macos/gnustep) it seems impossible to tell whether the difference in behaviors of the test app on the two systems is caused by a difference in KVO implementations or a difference in KVB implementations... That is to say, it's a test to demonstrate a gui bug without demonstrating whether the bug arises in the gui (KVB) or from an underlying fault in base (KVO).
I guess I'll have to look at the KVB code to see exactly what it does and produce a testcase for KVO.
|
Sat 01 Mar 2008 08:44:38 PM UTC, comment #3:
I was able to work around the problem with the checkbox being switched off immediately. It turns out that this was caused by a call to observeValueForKeyPath:ofObject:change:context: with an empty change information.
This happens as the same change notification gets send on three levels by NSKeyValueObserving, after the first one is sent the change info is reset in didChangeValueForKey: now the next two levels find an empty change info, still they send it on. I would claim that this is a wrong behaviour and that we should just not send the notification, when the change has been already reset to empty.
|
Thu 28 Feb 2008 07:48:46 PM UTC, comment #2:
Added a small test program to show the problem. This has a GNUmakefile as well as a XCode project file.
The behaviour is as described, when switching on the check box it switches off immediately. This happens only on GNUstep on Apple it behaves as expected.
(file #15136)
|
Tue 26 Feb 2008 11:22:39 AM UTC, comment #1:
I think we need a simple testcase for this, so we can try it on MacOS-X and see what the expected behavior actually is.
|
Thu 07 Feb 2008 08:42:00 AM UTC, original submission:
In gui I use KVO to implement KVB. The last change I made on that some time ago should allow a back chanel from the user inteface elements to the bound controller. Now this bit is failing. For a check box I get a strange effect. When I switch this checkbox on by clicking on it, it is shown on very shortly but gets switched off again immediatly. Debugging this showed that the call to setValue:For: on the controller results in three changes reported back to the user interface. One from the actual setSomething: method in the controller class (which is fine), one from takeValue:For: (which could be switched off, as far as I understand the code) and one from setValue:For:.
Now there should be a way that the KVO mechanism is able to tell that it already send the notification for a change and use the underlying methods of the base class to change the value, not the cooked up methods from the special class. But this isn't the real problem, what makes it worse for me is that the set value changes. This seems to happen in the takeValue:For: call, as I get the correct value once and then twice the wrong one.
It is this later behaviour that I don't understand. Anybody with an idea, what may be happening?
|