<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel><title>mikeash.com pyblog/key-value-observing-done-right.html comments</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>mikeash.com Recent Comments</description><lastBuildDate>Sun, 12 Apr 2026 03:12:47 GMT</lastBuildDate><generator>PyRSS2Gen-1.0.0</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>الوليد - 2016-09-30 15:56:19</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>I figured there was probably some objc_* invocation for such a situation, but I gave up pretty quickly after discovering little to no documentation for the new garbage collection APIs. I gave a try though, and it appears to work as expected.</description><guid isPermaLink="true">978774f19d59b40a58e76d6f5a197348</guid><pubDate>Fri, 30 Sep 2016 15:56:19 GMT</pubDate></item><item><title>YIem - 2016-05-18 02:39:46</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>Thank you very much, let me further understand KVO</description><guid isPermaLink="true">70502de9f27e563ae56ca2bfec927f8b</guid><pubDate>Wed, 18 May 2016 02:39:46 GMT</pubDate></item><item><title>Óscar Morales Vivó - 2011-10-09 20:59:51</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>Readers might want to note that as of Lion/iOS5 there's a -removeObserver:forKeyPath:context: that takes care of issue #3.
&lt;br /&gt;
&lt;br /&gt;With #3 taken care of and a little coder discipline the context pointer can find a use (dealing with #2) #1 can be partially taken care of. Everything still gets annoyingly funneled through ovserveValueFor… but by sending the class pointer as a context pointer to observe and checking for it at the observe method it is possible to avoid weird surprises.</description><guid isPermaLink="true">8c81f8ce9ef8409d73e39dfce36dff59</guid><pubDate>Sun, 09 Oct 2011 20:59:51 GMT</pubDate></item><item><title>taglud - 2010-10-27 06:55:59</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>my address is: taglud@gmail.com</description><guid isPermaLink="true">fcfb99270a38677819d765499277c228</guid><pubDate>Wed, 27 Oct 2010 06:55:59 GMT</pubDate></item><item><title>taglud - 2010-10-27 06:54:39</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NSString *action = (NSString *)context;
&lt;br /&gt;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if ([action isEqualToString:ANNOTATION_SELECTED_DESELECTED]) {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BOOL annotationSelected = [[change valueForKey:@"new"] boolValue];
&lt;br /&gt;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (annotationSelected) {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Actions when annotation selected.
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// I create the appropriate popover here and display it in self.view
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} else {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Actions when annotation deselected.
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NSLog(@"Annotation deselected! But never pass here...");
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&lt;br /&gt;}
&lt;br /&gt;
&lt;br /&gt;My problem is when my popover is dismissed, if I want to select the same annotation, it just doesn't work... Like if the state of the observer is still "activated". So in order to select my annotation, I need to select another one, and then I can select it again... It's annoying to not be able to select the same annotation twice in a row.
&lt;br /&gt;</description><guid isPermaLink="true">67fee1ae30dd887ef50271ab4ff2caac</guid><pubDate>Wed, 27 Oct 2010 06:54:39 GMT</pubDate></item><item><title>Frédéric Testuz - 2010-08-12 11:53:38</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>Inspire by this note and the new method for NSNotificationCenter in Snow Leopard, I did a simpler version with blocks. In my code, the observer objects are not collected. In consequence there is less problems with threading.
&lt;br /&gt;
&lt;br /&gt;&lt;a href="http://web.me.com/ftestuz/codes/FTZKeyValueObserver.zip"&gt;http://web.me.com/ftestuz/codes/FTZKeyValueObserver.zip&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;PS : I'm not used to share code, if I should have give credits in my code, please say it, I will correct the code.
&lt;br /&gt;
&lt;br /&gt;</description><guid isPermaLink="true">cca1c667620568b27026d7a85214ac6a</guid><pubDate>Thu, 12 Aug 2010 11:53:38 GMT</pubDate></item><item><title>Anonymous - 2010-05-21 15:09:48</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>Maybe I'm just too tired at the moment, but I don't follow your logic of the last few sentences of the "The context pointer is useless" bullet point. I don't see how you came to the conclusion that you can't store context in the context pointer. Could you clarify please?</description><guid isPermaLink="true">b56ed77709b21218f6868bbdffb1438b</guid><pubDate>Fri, 21 May 2010 15:09:48 GMT</pubDate></item><item><title>Gwynne Raskind - 2010-01-23 12:40:08</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>I've modified Jerry's version of the code further. It drops support for 10.4 and can use a block for the KVO notification under 10.6. I also included a few OCUnit tests and automation for building the DocSet from the header docs. It's posted at:
&lt;br /&gt;
&lt;br /&gt;&lt;a href="http://www.darkrainfall.org/code.html"&gt;http://www.darkrainfall.org/code.html&lt;/a&gt;</description><guid isPermaLink="true">b18e88067c6ac7dcb381c4f4ed2c756d</guid><pubDate>Sat, 23 Jan 2010 12:40:08 GMT</pubDate></item><item><title>halfactivist - 2009-11-13 13:34:50</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>Actually, I wrote a simple workaround to allow passing a selector and this where the context comes in handy: in observeValueForKeyPath just call the selector you passed.
&lt;br /&gt;You could even create a simple class that provides a selector and other info and pass it in the context.</description><guid isPermaLink="true">7ce34827c67b9a761b3dc3b834caf40b</guid><pubDate>Fri, 13 Nov 2009 13:34:50 GMT</pubDate></item><item><title>mikeash - 2009-10-13 03:13:35</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>My copy does not incorporate Jerry's changed. I wanted to keep mine simple. I considered Jerry's version to be a fork (and a nifty one!) and didn't merge it back in.</description><guid isPermaLink="true">1cfb524c5cc48a3637fb881d2053b11d</guid><pubDate>Tue, 13 Oct 2009 03:13:35 GMT</pubDate></item><item><title>Dave Camp - 2009-10-12 18:14:34</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>So, does your SVN repository incorporate the changes Jerry made? I'm comparing the current code with his .zip and there are a lot of differences...</description><guid isPermaLink="true">dabf8f4760bafac033f090d4bdec9e71</guid><pubDate>Mon, 12 Oct 2009 18:14:34 GMT</pubDate></item><item><title>mikeash - 2009-06-24 01:21:07</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>Good stuff. Returning the observation object makes sense. I don't like exposing that object to the outside world, but since you leave it untyped, it's not much of an exposure.
&lt;br /&gt;
&lt;br /&gt;Regarding your query about @synchronized, you are correct that its purpose is to protect the observations dictionary. Every access to that dictionary (read or write) must be wrapped in @synchronized(self). You missed one spot, on line 236 (in -removeObserver:object:keyPath:selector:). Everything else looks correct.
&lt;br /&gt;
&lt;br /&gt;Thanks for sharing your changes. It's gratifying to see people doing something useful with this code.</description><guid isPermaLink="true">861093e06e3b820d972ddfdb4ca1827b</guid><pubDate>Wed, 24 Jun 2009 01:21:07 GMT</pubDate></item><item><title>Jerry Krinock - 2009-06-24 00:56:32</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>Sorry, I pasted in my ftp destination instead of the link.  Try this instead:
&lt;br /&gt;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href="http://sheepsystems.com/files/MAKVONotificationCenter.zip"&gt;http://sheepsystems.com/files/MAKVONotificationCenter.zip&lt;/a&gt;</description><guid isPermaLink="true">74efb07d57e51094a13482cdc4cf9dda</guid><pubDate>Wed, 24 Jun 2009 00:56:32 GMT</pubDate></item><item><title>Jerry Krinock - 2009-06-24 00:55:13</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>I decided to solve both my problem of adding redundant (multiple, identical) observers and MAKVONotificationCenter's problem of not being able to handle them, with one solution.
&lt;br /&gt;
&lt;br /&gt;Well, I can think of no reason why an observer would want to receive the same observation twice in the same run loop iteration; it's a complete waste of CPU.  Therefore, in my patched code, before setting a new observer, MAKVONotificationCenter checks to see if an observer with the requested parameters has already been set and, if so, doesn't set it.  How will you know to not attempt to remove this observer which has not been set?  Well, addObserver::::: now returns the instance of MAKVObservation (formerly the "helper") which it set.  If it returns nil, you know that it didn't work and not to remove it.
&lt;br /&gt;
&lt;br /&gt;But that's what I call the Hard Way.  Rather than type all those parmeters again, you can stash the observation (into a mutable array instance variable, typically) and then when you're done observing, remove it using the new method, -[MAKVONotificationCenter removeObservation:].
&lt;br /&gt;
&lt;br /&gt;But that's what I call the Middle Way because there's now there's an even easier way.  A fourth irritating thing about Apple's KVO is that there is no way to remove all of an observer's observation at once, as you can with -[NSNotificationCenter removeObserver:].  This is usually what I want to do, in -dealloc or -didTurnIntoFault, when an object is going away, just remove all of its observations without having to worry about missing any of them.  Thanks to the centralized storage of MAKVONotificationCenter, you can now do this using the new method -[MAKVONotificationCenter removeObserver:].
&lt;br /&gt;
&lt;br /&gt;So, I tried this code in my actual project and it cured the crash I was getting yesterday.  I still need to try removing observers the Easy Way.
&lt;br /&gt;
&lt;br /&gt;Here's the project, with a new demo tool and documentation.  Probably I'll find some bugs today -- it's an alpha alpha.
&lt;br /&gt;&amp;nbsp;&amp;nbsp;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;sheepsystems.com/public_html/files/MAKVONotificationCenter.zip
&lt;br /&gt;
&lt;br /&gt;Mike, if you could glance at my use of @synchronized one of these days that would be good.  It looks like you were just trying to protect the helpers dictionary, but you know you're the multithreading guy.</description><guid isPermaLink="true">ff0d2fa0c2815f2c4163caaea850cce5</guid><pubDate>Wed, 24 Jun 2009 00:55:13 GMT</pubDate></item><item><title>Jerry Krinock - 2009-06-23 13:55:40</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>Regarding the question of what happens if you register the same observation twice using Apple's -addObserver:forKeyPath:options:context:, I wrote some bonehead code and tested it.  The answer is Mike is correct.  Your -observeValueForKeyPath:ofObject:change:context: gets invoked twice.  Also, you should invoke -removeObserver:forKeyPath: twice.  Invoke it three times and you'll get a crash.
&lt;br /&gt;
&lt;br /&gt;But the same experiment shows a problem when I try this with MAKVONotificationCenter.   When -[MAKVONotificationCenter addObserver:object:keyPath:selector:userInfo:options] is invoked for the second time, it sets a second helper into its _observerHelpers dictionary using the same key as the first helper, thus replacing it.  This causes the first helper to be deallocced, but it's still registered as an observer with Cocoa.  So when a change is observed, Cocoa attempts to send it a message  ... byebye! 
&lt;br /&gt;
&lt;br /&gt;Now, one might ask, who would be silly enough to register twice.  Well, I have a Chicken and and Egg, both managed objects in a relationship.  The user can create either the chicken or the egg first, and can change the chicken's egg, or the egg's chicken.  Whenever a chicken has an egg, however it got there, I want the the chicken to be observing the temperature of its egg.  In order to cover all the bases, I need to set this observation in several places: -[Chicken awakeFromInsert], -[Chicken awakeFromFetch], -[Egg awakeFromInsert], -[Egg awakeFromFetch]; also in the setters -[Chicken setEgg:] and -[Egg setChicken:] or else in the KVO methods observing these relationships.  The result is that sometimes two identical observations get set.  Maybe I could avoid the multiple identical observations somehow, but I haven't thought it all through yet.
&lt;br /&gt;
&lt;br /&gt;I'd like to patch MAKVONotificationCenter so it can handle the multiple identical observations like Apple's does, and have a couple ideas but the hour is too late here to be doing conceptual work.  I'll post now and see if anyone has any suggestions.</description><guid isPermaLink="true">c725ea36f22415152e31afb4c45e3d46</guid><pubDate>Tue, 23 Jun 2009 13:55:40 GMT</pubDate></item><item><title>mikeash - 2009-05-06 05:07:06</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>I don't see anything in the documentation that says what happens if you observe twice. Absent any documented behavior, my assumption would be that it registers you twice. That's what happens with notifications, and is the sensible thing to do, at least if the context pointers are different. Otherwise it would be impossible for a class and a subclass to both observe the same thing, and that's just no good.
&lt;br /&gt;
&lt;br /&gt;As for passing a selector as the context, that's pretty good. You'd have to be sure that no other class in your hierarchy would use it. In practice, using a unique selector name should be enough to guarantee that. (In other words, don't use @selector(description), use @selector(_myPrivateMethodNotApplesDoNotTouch).) I haven't seen this idea anywhere else before. I've seen a lot of talk about using a unique constant string so it should have been obvious, but wasn't.
&lt;br /&gt;
&lt;br /&gt;As for centralization, you can think of it as an implementation detail if it makes you feel better. I guarantee you that Apple's implementation of KVO has some kind of central registry, they just don't show it to you.</description><guid isPermaLink="true">4c10d9a9d0dd77c5f83c79e63d583772</guid><pubDate>Wed, 06 May 2009 05:07:06 GMT</pubDate></item><item><title>Brent Gulanowski - 2009-05-06 04:30:58</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>If the same object calls -addObserver:forKeypath:options:context: twice, doesn't the second registration replace the first? How can you bind to a keypath and observe it independently? The design of the API implies that doesn't work (and that your criticism of -removeObserver:forKeyPath: isn't valid)--the same object can only register to observe  the same key path of an observed object once. If I'm just plain wrong, and someone has a link to the correct page in the docs, I'd appreciate it. Maybe I'll re-read them anyway.
&lt;br /&gt;
&lt;br /&gt;As for problems 1 and 2, can't they be solved at the same time by using a selector (either SEL or string version) as the context? I only just thought of this. It's always bugged me having to write a switch or other logic in -observeValueForKeyPath: but I should have just included the selector as the context and then asked
&lt;br /&gt;&lt;div class="blogcommentquote"&gt;&lt;div class="blogcommentquoteinner"&gt; if([self responsdsToSelector:(SEL)context]) [self performSelector:(SEL)context withObject:object withObject:change];
&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;Still agree with you that the API could be improved a lot. But not with a notification center. Centralization is going the wrong direction.</description><guid isPermaLink="true">063b807107983d873680f4446a428e4e</guid><pubDate>Wed, 06 May 2009 04:30:58 GMT</pubDate></item><item><title>mikeash - 2008-11-18 00:49:47</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>Glad it worked. And just to correct my post above, such code would of course require &lt;i&gt;Leopard&lt;/i&gt;, and wouldn't work on Tiger. Not a big concern if you're writing GC code anyway, of course.</description><guid isPermaLink="true">8b9f7a314e79b05553358f14836e5bf8</guid><pubDate>Tue, 18 Nov 2008 00:49:47 GMT</pubDate></item><item><title>Brian Webster - 2008-11-18 00:29:14</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>Ah, I figured there was probably some objc_* invocation for such a situation, but I gave up pretty quickly after discovering little to no documentation for the new garbage collection APIs.  I gave a try though, and it appears to work as expected.</description><guid isPermaLink="true">e6dadcc8bcf65bbfe1c061be92829844</guid><pubDate>Tue, 18 Nov 2008 00:29:14 GMT</pubDate></item><item><title>mikeash - 2008-11-17 10:03:10</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>Nice detective work. I didn't really write with garbage collection in mind, but even if I had I would not have avoided this problem. The best workaround would be to use the objc_atomicCompareAndSwapGlobalBarrier() function defined in objc-atomic.h. As it is undocumented I can't be sure, but I believe this will also work in the non-GC case (although obviously such code would still require Tiger). Otherwise one function or the other could be chosen based on whether the collector is being used.
&lt;br /&gt;
&lt;br /&gt;Of course your workaround works just fine as well. I think it's slightly uglier, but I have nothing against ugly but functional code.</description><guid isPermaLink="true">8cf8fbe88163805f90227a2f0e149489</guid><pubDate>Mon, 17 Nov 2008 10:03:10 GMT</pubDate></item><item><title>Brian Webster - 2008-11-17 09:39:48</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>Thanks a lot for the code, Mike. I'd been using a similar concept in my own code to dispatch KVO observations to individual selectors, but was never really satisified with it. This technique is much more robust.
&lt;br /&gt;
&lt;br /&gt;I came across one problem in my application when using the code. The application uses Objective-C garbage collection, and while KVO would be triggered fine the first time a value changed, the notification would not come through on subsequent changes.  Upon investigation, I found that the helper object was being collected, and the KVO reference to it had been zeroed out.  
&lt;br /&gt;
&lt;br /&gt;It looks like the cause of this was the OSAtomicCompareAndSwapPtrBarrier() call in initializing the shared notification center, which does not pass through a garbage collection write barrier.  The whole notification center was being collected as a result, taking all the helper objects with it.  My solution was just to add a CFRetain(center) call after a successful assignment, and that seems to make things run smoothly.</description><guid isPermaLink="true">a8db552c683394dff1c2c8e52b0f24d3</guid><pubDate>Mon, 17 Nov 2008 09:39:48 GMT</pubDate></item><item><title>Jamie Kirkpatrick - 2008-11-02 19:47:07</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>Great idea Mike.  When I get back to Cocoa one of these days I'll be sure to use this.  
&lt;br /&gt;
&lt;br /&gt;Maybe you could point someone at Apple towards this piece in the hope that they might provide an improved API in the future? (or file a bug if that feels like it might work...)</description><guid isPermaLink="true">c6ae3aec297c2ece65d2b574814e9ff3</guid><pubDate>Sun, 02 Nov 2008 19:47:07 GMT</pubDate></item><item><title>charles - 2008-10-23 13:35:58</title><link>http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html#comments</link><description>I totally agree with this post, and I am thankful for the code sharing. I did a similar thing 2-3 years ago when dealing with the Xgrid APIs (for some reason, one of the classes in these APIs does have a delegate, but none of the others, all KVO... go figure). See GridEZ.framework.
&lt;br /&gt;
&lt;br /&gt;I believe some of the implementation choices made by Apple were based on performance (the KVO stuff gets called a lot by the UI, but maybe the fact that it gets called a lot is where they should have worked on), and was mostly designed to fit with all the bindings/InterfaceBuilder stuff. It works great for Interface Builder, but is horrible when you have to deal with it in code.</description><guid isPermaLink="true">a230e1fa20e0f5969c7e725e3815bf5d</guid><pubDate>Thu, 23 Oct 2008 13:35:58 GMT</pubDate></item></channel></rss>
