Do observers need to be removed in Swift 4/Objective-C? - objective-c

I'm trying to implement KVC/KVO in Swift 4. Much of the documentation I've read up on for KVC/KVO in Objective-C states that the observer needs to be removed when you are done with it. But, after looking at Apple's documentation of the implementation of KVO using Swift 4, they don't explicitly state whether or not the observer needs to be removed in a deinit method. They do not include a deinit method in the example class definition. But, I do not want to make any assumptions since all the Objective-C documentation I've read states that the observer needs to be removed.
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID12
I'm just not sure whether or not the observer needs to be removed through a deinit. Any help or point in the direction of a reference would be great, thanks.

If you are talking about NSKeyValueObservation: No they don't.
From the transcript of the WWDC 2017 Video "What is new in Foundation"
There is no need for a deinit where I throw away or tear down my observation because it's tied to the life cycle of that observation token. And so when the controller goes away, the observation token will go away.

The rules around deregistering observers was relaxed in 10.13, from the Foundation Release Notes (emphasis added):
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.
HTH

Related

Where is the official mention about observer retain/release of NSNotificationCenter method?

NSNotificationCenter is known not to retain/release the observers. It makes sense, and I also have experienced related issue.
Anyway today I realized that I really am not sure on this. The official documentation doesn't say anything about ref-counting stuffs.
Though its discussion section says that we have to unregister the observer before it deallocates, but it doesn't mean the center will not retain them. And deallocation timing can become different by the behavior of the center.
Then, I couldn't find anything from the documentation.
But in practice, we already know the add/remove observer method doesn't retain the observer by experience. Unretained reference is very exceptional behavior which may cause serious bug, then I believe that there should be an explicit mention on this.
Where is it?
No, observers are not retained. That's why when you make an object an observer, you must make sure that it stops observing in its dealloc method. Logically, an observed object should know nothing about the observer, shouldn't care about observers, and therefore shouldn't hold a strong reference to observers.

What kind of property should be set to nil in dealloc?

I'm using ARC. Will ARC automatically release all the properties in dealloc? Is it necessary to manual set all public properties and private field to nil? Are there any good pattern to follow?
Under ARC, the pattern is... don't do anything in dealloc, or even implement it. ARC takes care of your properties and instance variables for you.
The only exception is that dealloc is a good place to unregister for notifications, if your object has registered for any.
Good question. When using ARC the compiler will implement a dealloc method for you and will handle implicitly the release of your instance variables and properties.
You may still need a custom -dealloc if your class needs to do anything other than releasing memory (e.g unregister for notifications like jrturton mentioned).
You can get a good grasp of what's you need to consider when transitioning to ARC in those Apple official notes.

In ARC do we need to send removeObserver: explicitly?

Do we have to send removeObserver: explicitly for objects that have been added as observers to an NSNotificationCenter before?
I am bit confused and unable to find the exact answer for this.
Please provide me in detail, about this including why we need to removeObserver explicitly, and why don't compiler put it implicitly in class/application?
Yes, you need to call removeObserver:, if you don't the observed class could call all deallocated instance of the observer.
From 10.11 observers are not required to un-register in their deallocation method.
NSNotificationCenter and NSDistributedNotificationCenter no longer
send notifications to registered observers that may be deallocated. If
the observer is able to be stored as a zeroing-weak reference the
underlying storage stores the observer as a zeroing weak reference.
Alternatively, if the object cannot be stored weakly (because it has a
custom retain/release mechanism that would prevent the runtime from
being able to store the object weakly) the object is stored as a
non-weak zeroing reference. This means that observers are not required
to un-register in their deallocation method.[1]
Removing the observer is always a smart idea.
If you don't remove the observer, messages will still be sent, even if the object was deallocated. It might even be attached to another object, which would definitely lead to serious trouble.
You always need to remove observers for KVO as well as for Notifications.

How to manage unsafe_unretained ivars/properties?

I started objective-c and iOS a couple of weeks ago (worth bearing in mind), and I apologise in advance for the awful diagram!!
The above diagram shows the structure of my calls to a webservice. Thin arrows denote an object creating another object, whereas thick arrows denote an object holding a strong (retained) reference to the pointed-to object.
I believe that this contains what is called a "circular reference" and will create problems when it comes to deallocating the objects.
I understand that the easy answer would be to replace some of the strong references to weak ones, which I'd love to do, except my project is also targeting iOS 3.2 (not my decision - I can't really change this fact!). So, I think I'm right in saying that I have to use __unsafe_unretained instead, but I'm quite worried about the fact that these won't auto-zero, as I'll end up with EXC_BAD_ACCESS problems when objects get deallocated...
So my problem is firstly that I have circular references. To solve, I would have to use __unsafe_unretained, which leads to my second problem: How to correctly manage these?
A question that might be related is: How does NSURLConnection manage it's strong references? I have heard from various sources that it retains its delegate? So...if I retain an NSURLConnection, (and am also its delegate) and it retains me, this would also be a circular reference, no? How does it get around my problem?
Any advice is very welcome!
Regards,
Nick
When a parent has a reference to a child object, it should use a strong reference. When a child has a reference to it's parent object, it should use a weak reference, aka unsafe_unretained.
By convention, delegate relationships in iOS are usually weak references, so you'll find that most delegate properties on Apple's own classes are declared as unsafe_unretained.
So your controller retains the services that it is using, but the services only weakly link back to the controller. That way, if the controller is released, the whole lot can be safely disposed of without any circular references.
The danger with this is that if the web service is doing some long-running task, and the controller gets released before it has finished, the service is left with a dangling pointer to it's now-deallocated delegate. If it tries to send a message to the delegate, such as "I have finished" it will crash.
There are a few approaches to help solve this (they aren't mutually exclusive - you should try to do them all whenever possible):
1) Always set the delegate properties of your services to nil in your controller's dealloc method. This ensures that when the controller is released, the delegate references to it are set to nil (sort of a crude, manual equivalent of what ARC's weak references do automatically).
2) When creating your own service classes that have delegates, make them retain their delegate while they are running and then release the delegate when they are done. That way the delegate object can't get deallocated while the service is still sending it messages, but it will still get released once the service has finished (NSTimer's and NSURLConnections both work this way - they retain their delegate while they are running and release it when they are done).
3) Try not to have long-running services owned by something transient like a view controller. Consider creating singleton objects (shared static object instances) that own your services, that way the service can do it's job in the background regardless of what's going on in the view layer. The controller can still call the service, but doesn't own it - the service is owned by a static object that will exist for the duration that the app is running, and so there's no risk of leaks or premature releases. The service can communicate with the controller via NSNotifications instead of delegate calls, so there is no need for it to have a reference to an object that may vanish. NSNotifications are a great way to communicate between multiple classes without creating circular references.
All of your questions and concerns are correct, and this problem with the previous use of assign (now better named __unsafe_unretained) is why Apple developed auto-zeroing for weak. But we've dealt reasonably safely with assign delegates for many years, so as you suspect, there are ways to do it.
First, as a matter of practice, you should always clear yourself as the delegate when your release an object you were delegate for. Pre-ARC, this was traditionally done in dealloc:
- (void)dealloc {
[tableView_ setDelegate:nil];
[tableView_ release];
tableView_ = nil;
}
You should still include that setDelegate:nil in your dealloc if delegate is __unsafe_unretained. This will address the most common form of the problem (when the delegate is deallocated before the delegating object).
Regarding NSURLConnection, you are also correct that it retains its delegate. This is ok because it has a lifespan typically much shorter than its delegate (versus a table view delegate which almost always has the same lifespan as the table view). See " How to work around/handle delegation EXC_BAD_ACCESS errors? Obj C " for more discussion on this in a pre-ARC context (the same concepts apply in the new world of strong/weak).

NSProxy and Key Value Observing

NSProxy seems to work very well as stand-in objects for those that don't yet exist. For example.
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:self.target];
}
The above code will transparently pass any method invocation to the target that the proxy represents. However, it doesn't seem to handle KVO observations and notifications on the target. I tried to use a NSProxy subclass as standing for objects to be passed to NSTableView, but I'm getting the following error.
Cannot update for observer <NSAutounbinderObservance 0x105889dd0> for
the key path "objectValue.status" from <NSTableCellView 0x105886a80>,
most likely because the value for the key "objectValue" has changed
without an appropriate KVO notification being sent. Check the
KVO-compliance of the NSTableCellView class.
Is there a way to make transparent NSProxy that is KVO compliant?
The crux of the issue is that the guts of Key-Value Observing lives in NSObject, and NSProxy doesn't inherit from NSObject. I'm reasonably confident that any approach will require the NSProxy object to keep its own list of observances (i.e. what outside folks are hoping to observe about it.) This alone would add considerable weight to your NSProxy implementation.
Observe the target
It looks like you've already tried having observers of the proxy actually observe the real object -- in other words, if the target were always populated, and you simply forwarded all invocations to the target, you would also be forwarding addObserver:... and removeObserver:... calls. The problem with this is that you started out by saying:
NSProxy seems to work very well as stand-in objects for those that
don't yet exist
For completeness, I'll describe some of the guts of this approach and why it can't work (at least for the general case):
In order for this to work, your NSProxy subclass would have to collect invocations of the registration methods that were called before the target was set, and then pass them through to the target when it gets set. This quickly gets hairy when you consider that you must also process removals; you wouldn't want to add an observation that was subsequently removed (since the observing object could have been dealloc'ed). You also probably don't want your method of tracking observations to retain any of the observers, lest this create unintended retain cycles. I see the following possible transitions in target value that would need to be handled
Target was nil on init, becomes non-nil later
Target was set non-nil, becomes nil later
Target was set non-nil, then changes to another non-nil value
Target was nil (not on init), becomes non-nil later
...and we run into problems right away in case #1. We would probably be all right here if the KVO observer only observed objectValue (since that will always be your proxy), but say an observer has observed a keyPath that goes through your proxy/real-object, say objectValue.status. This means that the KVO machinery will have called valueForKey: objectValue on the target of the observation and gotten your proxy back, then it will call valueForKey: status on your proxy and will have gotten nil back. When the target becomes non-nil, KVO will have considered that value to have changed out from under it (i.e. not KVO compliant) and you'll get that error message you quoted. If you had a way to temporarily force the target to return nil for status, you could turn that behavior on, call -[target willChangeValueForKey: status], turn the behavior off, then call -[target didChangeValueForKey: status]. Anyway, we can stop here at case #1 because they have the same pitfalls:
nil won't do anything if you call willChangeValueForKey: on it (i.e. the KVO machinery will never know to update its internal state during a transition to or from nil)
forcing any target object to have a mechanism whereby it will temporarily lie and return nil from valueForKey: for all keys seems like a pretty onerous requirement, when the stated desire was a "transparent proxy".
what does it even mean to call setValue:forKey: on a proxy with a nil target? do we keep those values around? waiting for the real target? do we throw? Huge open issue.
One possible modification to this approach would be to use a surrogate target when the real target is nil, perhaps an empty NSMutableDictionary, and forward KVC/KVO invocations to the surrogate. This would solve the problem of not being able to meaningfully call willChangeValueForKey: on nil. All that said, assuming you've maintained your list of observations, I'm not optimistic that KVO will tolerate the following sequence that would be involved with setting the target here in case #1:
outside observer calls -[proxy addObserver:...], proxy forwards to dictionary surrogate
proxy calls -[surrogate willChangeValueForKey:] because target is being set
proxy calls -[surrogate removeObserver:...] on surrogate
proxy calls -[newTarget addObserver:...] on new target
proxy calls -[newTarget didChangeValueForKey:] to balance call #2
It's not clear to me that this won't also lead to the same error. This whole approach is really shaping up to be a hot mess, isn't it?
I did have a couple alternate ideas, but #1 is fairly trivial and #2 and #3 aren't simple enough or confidence-inspiring enough to make me want to burn the time to code them up. But, for posterity, how about:
1. Use NSObjectController for your proxy
Sure, it gums up your keyPaths with an extra key to get through the controller, but this is sort of NSObjectController's whole reason for being, right? It can have nil content, and will handle all the observation set up and tear-down. It doesn't achieve the goal of a transparent, invocation forwarding proxy, but for example, if the goal is to have a stand-in for some asynchronously generated object, it would probably be fairly straightforward to have the asynchronous generation operation deliver the final object to the controller. This is probably the lowest-effort approach, but doesn't really address the 'transparent' requirement.
2. Use an NSObject subclass for your proxy
NSProxy's primary feature isn't that it has some magic in it -- the primary feature is that it doesn't have (all) the NSObject implementation in it. If you're willing to go to the effort to override all NSObject behaviors that you don't want, and shunt them back around into your forwarding mechanism, you can end up with the same net value provided by NSProxy but with the KVO support mechanism left in place. From there, it's a matter of your proxy watching all the same key paths on the target that were observed on it, and then rebroadcasting willChange... and didChange... notifications from the target so that outside observers see them as coming from your proxy.
...and now for something really crazy:
3. (Ab)Use the runtime to bring the NSObject KVC/KVO behavior into your NSProxy subclass
You can use the runtime to get the method implementations related to KVC and KVO from NSObject (i.e. class_getMethodImplementation([NSObject class], #selector(addObserver:...))), and then you can add those methods (i.e. class_addMethod([MyProxy class], #selector(addObserver:...), imp, types)) to your proxy subclass.
This will likely lead to a guess-and-check process of figuring out all the private/internal methods on NSObject that the public KVO methods call, and then adding those to the list of methods that you wholesale over. It seems logical to assume that the internal data structures that maintain KVO observances would not be maintained in ivars of NSObject (NSObject.h indicates no ivars -- not that that means anything these days) since that would mean that every NSObject instance would pay the space price. Also, I see a lot of C functions in stack traces of KVO notifications. I think you could probably get to a point where you had brought in enough functionality for the NSProxy to be a first-class participant in KVO. From that point forward, this solution looks like the NSObject based solution; you observe the target and rebroadcast the notifications as if they came from you, additionally faking up willChange/didChange notifications around any changes to the target. You might even be able to automate some of this in your invocation forwarding mechanism by setting a flag when you enter any of the KVO public API calls, and then attempting to bring over all methods called on you until you clear the flag when the public API call returns -- the hitch there would be trying to guarantee that bringing over those methods didn't otherwise ruin the transparency of your proxy.
Where I suspect this will fall down is in the mechanism whereby KVO creates dynamic subclasses of your class at runtime. The details of that mechanism are opaque, and would probably lead to another long train of figuring out private/internal methods to bring in from NSObject. In the end, this approach is also completely fragile, lest any of the internal implementation details change.
...in conclusion
In the abstract, the problem boils down to the fact that KVO expects a coherent, knowable, consistently updated (via notifications) state across it's key space. (Add "mutable" to that list if you want to support -setValue:forKey: or editable bindings.) Barring dirty tricks, being first class participants means being NSObjects. If one of those steps in the chain implements it's functionality by calling through to some other internal state, that's its prerogative, but it'll be responsible for fulfilling all its obligations for KVO compliance.
For that reason, I posit that if any of these solutions are worth the effort, I'd put my money on the "using an NSObject as the proxy and not NSProxy." So to get to the exact nature of your question, there may be a way to make an NSProxy subclass that is KVO compliant, but it hardly seems like it would worth it.
I don't have the exact same use case (no bindings) of OP but mine was similar: I am creating an NSProxy subclass that presents itself as another object that is actually loaded from a server. During the load, other objects can subscribe to the proxy and the proxy will forward the KVO as soon as the object arrives.
There is a simple NSArray property in the proxy that records all observers. Until the real object is loaded, the proxy returns nil in valueForKey:. When the realObject arrives, the proxy calls addObserver:forKeyPath:options:context: on the real object and then, through the magic of the runtime, walks through all properties of realObject and does this:
id old = object_getIvar(realObject, backingVar);
object_setIvar(realObject, backingVar, nil);
[realObject willChangeValueForKey:propertyName];
object_setIvar(realObject, backingVar, old);
[realObject didChangeValueForKey:propertyName];
This seems to work, at least I haven't gotten any KVO compliance errors yet. It does make sense though, first all properties are nil and then they change from nil to the actual value. It is all like ipmcc said in his first statement above, so this post is just a confirmation! Note that the second surrogate that he proposed actually isn't needed, you just have to keep track of observers yourself.