Changing a property right after removing observers still sends out a KVO Notification - objective-c

I have some scenarios where a property is changing, but I do not want to notify observers of the change.
Here is what the code looks like:
[self.fontColorWell removeObserver:self.toolController forKeyPath:#"color"];
[self.fontColorWell setColor:[self.toolController valueForKey:[self fontColorKeyPath]]];
Unfortunately, when the second line executes, the observer (self.toolController) on self.fontColorWell still gets notified.
I'm pretty sure I know why this is happening, because KVO works by the runtime subclassing the observed class, and overridding the observed property to add didChange and willChange messages. (https://www.mikeash.com/pyblog/friday-qa-2009-01-23.html)
If I set a breakpoint at the second line, I can see clearly the class type of self.fontColorWell: NSKVONotifying_NSColorWell. So when -setColor is called, it's still called on the runtime subclass, which has the overridden setter that notifies observers.
But I figured that because I called removeObserver right before, shouldn't the observer be removed from the KVO lookup table, and thus even though the KVO object notifies observers, my self.toolController shouldn't be notified?
I know 100% that when -setColor is called, self.toolController immediately receives the -observeValueForKeyPath... message. I also am positive that all of there are no extra instances of my objects hanging around
Am I just not allowed to do what I'm trying to do? Or am I going about this the wrong way?

Related

Observers remaining in dispatch table after object removed

Kind of a newbie question, so forgive me if I'm missing some basic concepts...
I have a view controller within a window, which contains multiple NSTableViews, each of which having a variety of associated observers added via addObserver. These work fine and as expected, with notifications going to each of the correct table views. However, if I close the window, then open a new window (of the same type), posted notifications are being sent to the table views of the window that was previously closed.
I was under the impression that as of macOS 10.11 observers did not have to be explicitly removed, which I would assume would happen when the original window is closed... UNLESS I'm missing something fundamental about closing windows. At the moment, I don't do anything special when the user closes a window, and the window just vanished from the screen. Do the views created in my viewDidLoad method live on even after the window is closed? Or no I need to explicitly dispose of these views in a method such as viewWIllDisappear?
Thanks!
Even when object you added as observer is disposed the observer is not. According to official documentation you have to pair each addObserver with corresponding removeObserver. This is true and for NotificationCenter and for KVO. By the way adding object as observer does not increase its owners, so under correct memory management left observer result in run-time crash - that is why needed paired removeObserver (if there is no crash in such situation it means there is leak).
The place where to do this depends on usage. If you add observer in viewWillAppear then it is better to remove it in viewWillDisappear, if you add on creation then remove should be done in deinit

Do I have to removeObserver in KVO manually now?

I'm confused that do I have have to removeObserver in KVO manually now? When I search this question on the website, I found Do I have to removeObserver in KVO manually this question. So I tried to add NSKVODeallocateBreak symbolic exception. But nothing happened when I set observer to nil without remove the observer manually. Then I tried watch the debug memory graph for NSKeyValueObservationInfo.
This screenshot is captured after adding an observer, and I try to update observed property's value for tests.
Then I remove the observer. It shows something in the memory disappeared.
Then I set the observer to nil. It seems nothing happened.
After that, I initialize the observer again, and add observer again. It shows something in the memory appeared again.
At last, I just set the observer to nil (without removeObserver). It seems same as the result with removeObserver.
So, is there anything changed in KVO? Or my test is not correct?
My code is paste here: https://gist.github.com/kingcos/36575befa94a464d7aff689daa34f5d6

KVO produces error/not receiving notification

I have a class (call it classA) that contains a property named info (a model class, containing lots of info), wich in turn contains a property named name (a string). I want another class (classB) to receive a KVO notification when the string name changes in classA.
This is what I'm doing now on classB:
[classA addObserver: self forKeyPath: #"info.name" options: 0 context: nil];
There are two ways the value name changes on classA: when it is set directly like classA.info.name = ... and when info is set like classA.info = ...
When name is changed directly KVO works perfectly. However, when the info property is set and name changes indirectly, I get this error:
Cannot update for observer <classB> for the key path "info.name" from <classA>, most likely because the value for the key "info" has changed without an appropriate KVO notification being sent. Check the KVO-compliance of the classA class.
What should I change on classA to make this work?
The cause of this issue comes from the fact that you are implementing the setter -setInfo: for info and calling willChangeValueForKey: | didChangeValueForKey: inside it.
I've seen many instances in which it is believed that the will|didChange... calls need to be implemented for the KVO notification to be fired. This is true only when the setter is not explicitly called. When you do call the setter, the KVO mechanism takes care of firing the notification.
Most of the times leaving these calls in the setter is harmless and just causes extra notifications, but as seen in this case, it does cause a problem when updating a keypath -- as opposed to a key.
In short, if you do implement a setter, do not call will|didChangeValueForKey: inside of it.
Options cannot be 0, I think.
Its an old API, back before Apple got diligent about API design, and you have to give it something.

dealloc is being called and I am not sure why

I have a view with some buttons, text fields, and methods. When I load the view, switch to another view, and then switch back, my app crashes. I added in an NSLog in each method to see what the last method call before the crash was, and it was -(void)dealloc{
I am wondering why this method was called? Is it called every time you reload a view? I've double checked my code and I definitely do not call it anywhere.
EDIT : Found my problem, I was releasing an array that I was using to store views. Thanks to #Darren I traced my problem.
Dealloc is called when a class is no longer needed and removed from memory.
When you have no more pointers holding onto anything in the view, then it's dealocated.
How are you switching to/from the view?
if you set a (strong) pointer to the view then it won't be dealocated automatically.
-dealloc is called whenever an object's reference count drops to 0. To find your problem, figure out what object's -dealloc was called. What's the second method on the call stack? The third? Was -dealloc sent to a valid object pointer in the first place?
There are several ways to approach this sort of thing. A good first step is to turn on NSZombies (Google for it). That'll let you know if you're sending a message (like, say, dealloc) to an invalid object. Usually, that causes a crash, but with NSZombies you'll get a nice error message instead.

"message sent to deallocated instance 0xec75b0", but 0xec75b0 shouldn't even be visible

I'm working on an iPhone app that performs some communication with the server to be initialized the first time it's run. I have a class ServerCommunication with methods for each of the basic tasks to be performed (authenticate user, get activation key, download updates, etc.), and a new instance of ServerCommunication is created for each task; I'm not reusing them, although it would be more efficient.
When the user completes the first initialization screen, ServerCommunication gets created four times. I keep track of it with NSLog(#"Initializing ServerCommunication instance %p", self); in its -init method. The second initialization screen also calls ServerCommunication a few times when the user taps the "Next" button, but on its last instantiation, the app hangs with the message -[ServerCommunication insertDataIntoLocalDB:]: message sent to deallocated instance 0xec75b0 in the console. The thing is, 0xec75b0 is the address of the first instance of ServerCommunication I created way back at the first screen.
Why would it be sending messages to that instance? I'm not retaining them anywhere; they're mostly autoreleased. If it helps, all of the methods in that class perform asynchronous downloading of XML data with NSURLConnection and then parse it with NSXMLParser. The parser's delegate method -(void)parserDidEndDocument:(NSXMLParser *)parser then sends off NSNotifications that are received by methods in my view controllers so they know whether to proceed to the next screen or stay there and display an error message.
Help is much appreciated!
The first thing I would do is turn on NSZombies, which should let you break at the point where your zombie is being messaged.
A common cause of problems like this is when you have objects with weak references to each other that are not allocated and deallocated at the same time. So (hypothetically), some other object stores a pointer to your ServerCommunication object as a delegate or owner. When ServerCommunication is deallocated, it doesn't unregister, and then some time down the road the object holding the weak reference tries to message you.
If I had to completely guess (and I do!) I bet you add your ServerCommunication objects as an NSNotification observer, but never remove them. Try making sure that you do this:
[[NSNotificationCenter defaultCenter] removeObserver:self];
sometime before deallocation. (It's also possible that there's a more circuitous path involving NSNotification here -- such as a pointer to the ServerCommunication object being passed as data to the view controller, which is then trying to message it.)