Issue with KVO and replication when using custom dispatchQueue

32 views
Skip to first unread message

Brendan Duddridge

unread,
Nov 16, 2015, 2:23:30 AM11/16/15
to Couchbase Mobile
Hi,

So I'm using a custom dispatchQueue on my CBLDatabase in order to be able to process large numbers of CBLModel objects. For example, I may be importing thousands of lines from a CSV file or migrating to Couchbase from an older version of my database schema. I want these large database operations to be performed on a background thread so that I can have the main thread update things like progress bars and status updates.

That all works great so far.

However, I ran into a problem with replication. Because replication happens on a background thread, the KVO notifications of objects being created isn't happening on the main thread so I'm getting crashes.

The debugger tends to stop on the willChangeValueForKey line of  the following snippet in CBLModel in the document:didChange: method.

    // Prepare to send KVO notifications about all my properties in case they changed:

    NSSet* keys = [[self class] propertyNames];

    for (NSString* key in keys)

        [self willChangeValueForKey: key];



To work around this problem I've stopped using my custom dispatchQueue, but that also means I am unable to update the UI to show progress indicators and status messages.

Is there a proper way to be showing progress indicators without having the database access go through a custom dispatchQueue?

thanks,

Brendan

Jordan Wood

unread,
Nov 16, 2015, 11:40:05 AM11/16/15
to Couchbase Mobile
We are just moving our app from Firebase to Couchbase, and I was just about to change the code to use a custom dispatch queue for performance reasons.  Does running on a custom dispatch queue actually work?

Thanks!

Jordan

Jens Alfke

unread,
Nov 16, 2015, 11:51:22 AM11/16/15
to mobile-c...@googlegroups.com

On Nov 15, 2015, at 11:23 PM, Brendan Duddridge <bren...@gmail.com> wrote:

However, I ran into a problem with replication. Because replication happens on a background thread, the KVO notifications of objects being created isn't happening on the main thread so I'm getting crashes.

The notifications happen on the database's thread/queue. So if you’re using a custom dispatchQueue, that’s where the notifications will be delivered.

To work around this problem I've stopped using my custom dispatchQueue, but that also means I am unable to update the UI to show progress indicators and status messages.

I don’t understand … why are you unable?

—Jens

Jens Alfke

unread,
Nov 16, 2015, 11:52:46 AM11/16/15
to mobile-c...@googlegroups.com

On Nov 16, 2015, at 6:21 AM, Jordan Wood <wood.p...@gmail.com> wrote:

We are just moving our app from Firebase to Couchbase, and I was just about to change the code to use a custom dispatch queue for performance reasons.  Does running on a custom dispatch queue actually work?

Yes, it works. But you have to be careful to access the CBL objects only on their queue, since (like most Cocoa classes) they’re not thread-safe.

—Jens

Brendan Duddridge

unread,
Nov 16, 2015, 1:57:29 PM11/16/15
to Couchbase Mobile
I don’t understand … why are you unable?


If a big database transaction is occurring on the main thread, how can I update the UI at the same time? Basically as I'm fetching objects from the database, I post notifications or update an NSProgress object which should update the UI, but that just doesn't happen if I don't use the dispatchQueue. When database access is happening on a background thread, I'm able to post notifications to the main thread to update the UI. But if database access is happening on the main thread, those notifications don't run until after and then it's too late because it's over by then. Basically I just see no progress activity until it's done.

I also have bindings to my model objects that display things such as the name of a form or the name of a field. So when KVO updates those from a background thread, that's where the crashes happen as it's trying to update the UI because of the notifications.

Thanks,

Brendan

Jordan Wood

unread,
Nov 16, 2015, 3:02:57 PM11/16/15
to Couchbase Mobile
Why can't you do your UI updates from your custom dispatch queue by enclosing them in dispatch_async blocks that execute on the main dispatch queue?

Jordan

Jens Alfke

unread,
Nov 16, 2015, 5:01:33 PM11/16/15
to mobile-c...@googlegroups.com

On Nov 16, 2015, at 10:57 AM, Brendan Duddridge <bren...@gmail.com> wrote:

I also have bindings to my model objects that display things such as the name of a form or the name of a field. So when KVO updates those from a background thread, that's where the crashes happen as it's trying to update the UI because of the notifications.

This is one of those problems where the solution is “then don’t do that!” Those model objects are associated with a background thread. Your UI controls are associated with the main thread. So you can’t use a synchronous mechanism like bindings to connect them, because you’ll end up messaging objects on the wrong thread.

The way to do this is to have two CBLManagers, one on the main thread and one on your background dispatch queue. You use the one on the main thread and its CBL objects to drive your UI. You use the ones on the background thread/queue to do background operations.

—Jens

Brendan Duddridge

unread,
Nov 16, 2015, 5:23:04 PM11/16/15
to Couchbase Mobile
Hi Jordan,

I do normally do that, but with Cocoa bindings, I've bound to my model objects which get updated automatically when the willChangeValue method gets called from the background thread.

I'll try Jens' solution with having two CBLManagers.

Thanks!

Brendan

Jens Alfke

unread,
Nov 16, 2015, 6:24:36 PM11/16/15
to mobile-c...@googlegroups.com
Or if there’s just one operation you need to do in the background, you can use just one CBLManager but tell it to run an operation on the shared background thread:

/** Asynchronously dispatches a block to run on a background thread. The block will be given a
    CBLDatabase instance to use; <em>it must use that database instead of any CBL objects that are
    in use on the surrounding code's thread.</em> Otherwise thread-safety will be violated, and
    Really Bad Things that are intermittent and hard to debug can happen.
    (Note: Unlike most of the API, this method is thread-safe.) */
- (void) backgroundTellDatabaseNamed: (NSString*)dbName to: (void (^)(CBLDatabase*))block;

The only caveat is that this runs on the same thread as replication and asynchronous queries, so it will block progress on those till it completes.

—Jens
Reply all
Reply to author
Forward
0 new messages