-
Notifications
You must be signed in to change notification settings - Fork 301
Description
Summary:
On Apple platforms, a "token" such as NSKeyValueObservation automatically releases associated resources and cancels associated activities when that token is deallocated. You do not manually call remove() on the token.
Couchbase's Swift SDK does not follow this pattern. In your SDK, you MUST call remove() before allowing the token to go out of scope, or you have a permanent leak and Couchbase activities will continue infinitely in the background.
Why This Design Should Change:
-
It does not follow platform conventions, which makes it likely that developers who are used to the mental model of: "Activity X will continue as long as I hold a strong reference to this token and X will stop when I release that reference" are set up for failure.
-
The current design makes it difficult to reliably shut down Couchbase. You store a
ListenerTokenas an iVar on some object such asDatabaseController. You can't just releaseDatabaseController, which in turn releases theListenerTokeniVars and shuts down Couchbase activities. You have to manually check for the tokens, callremove()on each, etc. You cannot do that indeinit, so you must add some kind of-closeCouchbase()method that cleans up all the tokens and remember to call that method before you nil yourDatabaseController. That's fragile and complicated.
How ListenerToken Should Work:
The ListenerToken should work like other "tokens" in Swift: you keep a strong reference to the token for as long as you want the associated activity to continue. To stop/cancel the activity, you merely release the strong reference. In short, the work in remove() should be done automatically when the ListenerToken is deallocated.
Backwards Compatibility
This change is technically breaking, but only if someone is currently discarding ListenerTokens and purposefully allowing Couchbase to run infinitely without the ability to stop it. Any developer who is keeping a reference to the ListenerToken in order to call remove() at some point would be unaffected by this change.
Context
When the Replicator fails to connect to Sync Gateway, it retries at increasing intervals. I was surprised to see these retries continue even though every reference I held was nil-ed. I discovered that the ListenerToken was the culprit: you cannot simply let it go out of scope.