Do I have to removeObserver in KVO manually - ios

I have read the apple document about KVO, and it said:
Note: The key-value observing addObserver:forKeyPath:options:context:
method does not maintain strong references to the observing object,
the observed objects, or the context. You should ensure that you
maintain strong references to the observing, and observed, objects,
and the context as necessary.
The observer object not have a strong references to the observered object.
Does this man I can not call removeObserver:forKeyPath: in the dealloc method? Can it remove the observer automatically?

You must call -removeObserver:forKeyPath: manaully. iOS will not do it automatically.
Apple said does not maintain strong references to the observing object. I think it means, if you want to removeObserver for a temp var out off the temp var's scope, you should make the temp var as ivar, so you maintain the ivar's strong references.
If you do not call -removeObserver:forKeyPath:. You will make : 1) Something leak
such as you code like this :
[self addObserver:a forKeyPath:#"name" options:NSKeyValueObservingOptionNew context:nil];
if you do not call -removeObserver:forKeyPath:. It will console that :
An instance 0x756a1d0 of class MyClass was deallocated while key value observers were still registered with it. Observation info was
leaked, and may even become mistakenly attached to some other object.
Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger.
Here's the current observation info:
[NSKeyValueObservationInfo 0x7574f60] (
[NSKeyValueObservance 0x7574f20: Observer: 0x7568280, Key path: pageCount, Options: [New: YES, Old: NO, Prior: NO] Context: 0x0,
Property: 0x7574fa0]
)
When you debug it, you will find : The self and the a are not leaking. The leaking thing is the NSKeyValueObservationInfo object
If you do not call -removeObserver:forKeyPath:. You will make : 2) Intermediate class never destroy && Infinity notification
As the Apple document about KVO says:
When an observer is registered for an attribute of an object the isa
pointer of the observed object is modified, pointing to an
intermediate class rather than at the true class.
When you removeObserver, if no observer is registered, the intermediate class will destroy. And If you not call removeObserver, the intermediate class will never destroy and when you change the property, the setter method of intermediate class will continue to send notifications.

removeObserver:forKeyPath: has nothing to do with memory management or maintaining references. It just tells the runtime that your object no longer needs to be informed of changes to the object at that keyPath.

No, you must call -removerObserver:forKeyPath: when it is no longer needed, else the KVO system will have some dangling pointers that may leak or become attached to another object that doesn't expect it.

no, you have to call it.
not strong is NOT always weak
but in this case it means unsafe_unretained.
If you don't remove the observer you get an error message: "the object XY was deallocated while there was still a observer"
AND it might crash

Related

What is the appropriate place to remove KVO observer for a Managed Object

I'd like to monitor property value change of a managed object in Core Data for data integrity purpose (for example, if a specific property changes, another object in a relationship should be changed accordingly). I'm thinking to implement it using KVO and let the managed object observe its own property change. Below is my code.
extension Book {
override public func awakeFromInsert() {
super.awakeFromInsert()
observation = self.observe(\.date, options: [.old, .new]) { book, dateChange in
print("date is changed!")
}
}
override public func awakeFromFetch() {
super.awakeFromFetch()
observation = self.observe(\.date, options: [.old, .new]) { book, dateChange in
print("date is changed!")
}
}
}
(Note: the observation variable is a transient attribute of the managed object to keep a strong reference to the NSKeyValueObservation object.)
My question is what's the appropriate place to remove observers? Or is it necessary at all? I did try the above code and it worked fine. But I'm not sure if it just happened to work and may have potential issue when code becomes complex.
I have figured out the following after some research:
1) App should remove KVO observers before a NSObject is deallocated. For example, the doc says:
An object that calls this method must also eventually call either the removeObserver(:forKeyPath:) or removeObserver(:forKeyPath:context:) method to unregister the observer when participating in KVO.
Note: the doc is about addObserver(), not the API I'm using, but I think the idea is same.
2) The new KVO API in Swift removes the observer automatically when NSKeyValueObservation object is deinitialized (i.e., when it's out of scope or not referenced by others).
So I thought I should do this:
extension Book {
deinit {
observation = nil
}
}
But unfortunately that is impossible because XCode gave this error:
Deinitializers may only be declared within a class
So I wonder what's the usual way to do it? Could it be that, since the the observer and observee are same object, the underlying KVO framework code notices that observer object is also being deallocated, so it's OK without removing the observer explicitly?
Any suggestion will be much appreciated.
Update 1:
#pbasdf suggests using willTurnIntoFault. I google about it and find few discussions which indicate people using it this way. My main confusion: is fault a required a state before a NSManagemObject is deallocated? I don't find Apple doc says so (the doc doesn't have a state transition diagram).
EDIT: According to this post, when a NSManagedObjectContext is tore down, it turns all objects registered with it back into faults. If so, fault does always occur before deallocation.
BTW, while reading Apple doc, I also find this:
When Core Data turns an object into a fault, key-value observing (KVO) change notifications are sent to the object’s properties. If you are observing properties of an object that is turned into a fault and the fault is subsequently realized, you receive change notifications for properties whose values have not in fact changed.
That leads me to think using KVO with NSManagemObject is perhaps not a ideal approach because it is interfered by fault.
Update 2:
Since the observer and observee is the same object in my case, there is a much simpler approach than using KVO. That's defining a wrapper of property setter method and implementing all the logic in the wrapper. I'll use this approach.
That said, I still think my question is valid in general case where observer and observee are different objects.
Update 3:
#matt pointed out that it's OK to not remove KVO observers since iOS11. See another discussion here and official doc here.
Relaxed Key-Value Observing Unregistration Requirements
Prior to 10.13, KVO would throw an exception if any observers were still registered after an autonotifying object's -dealloc finished running. Additionally, if all observers were removed, but some were removed from another thread during dealloc, the exception would incorrectly still be thrown. This requirement has been relaxed in 10.13, subject to two conditions:
The object must be using KVO autonotifying, rather than manually calling -will and -didChangeValueForKey: (i.e. it should not return NO from +automaticallyNotifiesObserversForKey:)
The object must not override the (private) accessors for internal KVO state
If all of these are true, any remaining observers after -dealloc returns will be cleaned up by KVO; this is also somewhat more efficient than repeatedly calling -removeObserver methods.

Why am I crashing when trying to create a weak reference to self while dealloc is happening on another thread?

Background
Our app has a class that attempts to implement the Receptionist Pattern for KVO observation. Other classes throughout the app (such as view controllers) create instances of this one Receptionist class to serve as the KVO observer. Each Receptionist instance keeps a copy of a block provided by the owner, which the Receptionist instance will invoke on the proper operation queue when a KVO notification arrives.
The Receptionist's dealloc method invokes the KVO removeObserver method. The Owner keeps the Receptionist instance as a strong-reference field, so when the Owner is deallocated, the Receptionist will remove itself as an observer in the process of being deallocated.
The Crash
We're seeing reports from the field of crashes when the KVO notification is received by a Receptionist instance on one thread while the same instance's dealloc is in progress on another thread. The Receptionist's implementation of observeValueForKeyPath:ofObject:change:context: is crashing on this line:
__weak typeof(self) weakSelf = self;
The stack trace in the crash report shows this as a call to objc_initWeak, which calls weak_register_no_lock, which calls _objc_fatal.
The object whose key is being observed by this particular Receptionist is never deallocated. The Owner is also not being deallocated; the Owner is replacing this Receptionist instance with a different one.
The Confusion
I can understand that it's not useful to create a weak reference to an object that's already being deallocated, but I would expect weakSelf to receive a nil value, not to cause a crash.
The documentation for objc_initWeak explicitly mentions setting the target to null if the argument to which the reference is desired has begun deallocation. That sounds like the desired behavior, but I don't think it's what I'm seeing. I'm not keen to replace that line with an explicit call to objc_initWeak, since I doubt I'd manage the deallocation properly.
Could it really be the Receptionist's responsibility to notice that its own deallocation is in progress before requesting a weak reference to self? I would assume that there's some window between when an NSObject's deallocation starts and when that object's dealloc method is called, so signalling within the object from the dealloc method sounds flaky.
Thank you for reading!
PS: heavily edited after reading the questions raised by Ken Thomases.
This has nothing to do with the creation of the weak reference. The line you cite should only be run in a context where something has a strong reference to self.
Think about it: the crash that you're seeing may be during that line within your observeValueForKeyPath:ofObject:change:context: implementation, but, since there's clearly a race between deallocation and the call of that method, the deallocation could also occur during dispatch of that method call (or some other point). You're vulnerable to different crashes. So, no changes to the implementation of the method could possibly fix the problem, since the problem could manifest before your method is even called.
It's your responsibility to keep a strong reference to an object if you're going to be calling methods on that object. Or, from the other perspective, to avoid calling methods on object pointers that you're not sure will live for the duration of the call (because you hold a strong reference or some other API guarantee).
With KVO, you need to remove observers before releasing your last strong reference.

Remove all observers from a class

In a project I have a class say "A", there are many other class that are observing class "A"'s property values.
Sometimes the class "A" instance gets deallocated and observees fails resulting in crash!
Is there any way to remove all the observers from class "A"? Something like this:
-(void) dealloc{
[remove allObservers forKey:#"theKey"];
}
In short, sadly no. KVO is made so that it gets you in the end, unfortunately.
I've struggled with this thing before, and I found the following two solutions:
Use a proxy method to register for observation in your observee, which will maintain a list of weak references to observers.
Ideally, you'd want a proxy method for removing observers as well, so that your list is updated accordingly (although, since they are weak references in your list, it wouldn't hurt if some observer removed itself using standard KVO instead of your proxy method, and then deallocd itself).
In case your observee gets deallocated, it should inform all observers (using a protocol), or just remove them outright itself. For the last one, using exceptions might come in handy as well (I am aware that exceptions are evil in Obj-C, but what to do):
#try
{
[self removeObserver:observee forKeyPath:#"path"];
}
#catch (NSException * __unused exception) {}
Use some abstraction from KVO. There are a couple of projects that come to mind such as RZDataBinding and MAKVONotificationCenter (despite it's name, it actually pertains to KVO)
You should keep "A" class alive until there are other objects observing its property values. Maybe it gets deallocated because you're not correctly handling its reference.
You should check if "A" needs a 'strong' reference. When you don't need "A" anymore (i.e.: you're popping a view controller, you are refreshing a table, you're clearing a scrollview), you should also remove any observer attached to it (and be able to do it).

Im confused on how can I manipulate the properties inside an instance when it gets deinitialized?

I have this from either the Apple documentation or the swift book
When an instance gets deinitialized, you still have access to the
properties inside the instance and can manipulate them as needed
before the instance totally goes away.
I'm confused, do they mean when we for example do some mathematical action using the instances property in the deinit() method? or lets say when we print a property of type string that was part of a specific instance, also from the deinit() method?
If so, then is the deinit() method the only way to manipulate a property when it is being deinitialized?
if you have a swift class with a a var you know you have to clean up after because ARC can't free it (e.g. C memory), you can still do that in deinit. The pointers stored in the properties are still valid!
it isn't useful for much more though (ok end observing with the notification center or kvo) BECAUSE there is no guarantee WHEN deist is called. ONLY that it is called before deallocation [whenever that is]
deinit is called right before deallocation (when the retainCount reaches 0), so all your properties are still valid and you can print your string. You don't need to set properties to nil explicitly in deinit as that happens automatically.
This being said, most classes don't even need deinit implemented
Most of the time I used deinit to remove observer that the instance is registered to, post any notifications if needed, and things like that.
As far as I know, the deinit method gets called just before the instance gets deinitialized, to give you a final opportuninty to do whatever you need to do (cleanup, close a file, terminate a network connection, etc).
What the documentation says is that, at the time deinit is called your object has not been deinitialized yet (but will be very soon), so you still can (for the last time) access its properties.

How do I make sure to removeObserver without subclassing and overrideing dealloc?

When using the NSNotificationCenter object, if you add an observer for a notification, You must invoke removeObserver: or removeObserver:name:object: before any object specified by addObserverForName:object:queue:usingBlock: is deallocated.
Is there a way to do this without subclassing the object and overriding "dealloc"? I don't want to have to subclass a bunch of objects just to use notifications with them if I can avoid it. Is there some other way to learn about the dealloc before it happens?
Thanks for your help.
Generally speaking you should think about what object is observing changes and make it appropriate. This usually means a controller class, and in this case you usually already have a custom subclass. It should usually be receiving the observation callbacks and forwarding them as required. It should add and remove itself as an observer when it is created / destroyed / put on display / removed from display.
That said, if you persist with making arbitrary classes observers, here's an idea (I think it should be safe, though I wouldn't want to rely on it):
You can use a single custom class to handle the removal of the observer. This should be a class which is configured with a reference to the class to be removed. During the configuration method this class adds itself to the class to be removed using objc_setassociatedobject and OBJC_ASSOCIATION_RETAIN, and stores the pointer to the class to be removed in an NSValue. Then, when it is deallocated, it removes the other class from the notification center, using the NSValue.

Resources