-
Notifications
You must be signed in to change notification settings - Fork 35
New Threading and Context Stack model
One of the primary pieces of confusion about how to use CDQ comes up around threading, specifically, doing work anywhere but the main thread. The CDQ API hasn't supported it very well, and starting in 1.0, we aim to fix that.
There are two basic models for working in the background: persistent threads, and one-off asynchronous tasks. We're going to look at the latter first. Here's what you had to do in 0.x:
# Create a new private queue context with the main context
# as its parent, then pop it back off the stack.
cdq.contexts.new(NSPrivateQueueConcurrencyType) do
context = cdq.contexts.current
# any work on a private context must be passed to performBlock
context.performBlock(-> {
cdq.contexts.push(context) do
# your stuff
cdq.save(always_wait: true) # only save the current thread's stack
# Go save the main context on the main thread
Dispatch::Queue.main.async do
cdq.save
end
end
})
end
This works fine, but it's a tad obscure. You have to push the context around
yourself because it (intentionally) gets lost when you switch threads, and the
use of the new
method is confusing. So starting in version 1.0, this how you
would do the same thing:
cdq.background do
# your stuff
cdq.save(always_wait: true) # only save the current thread's stack
# Go save the main context on the main thread (optional)
cdq.save(:main)
end
We're also deprecating new
in favor of push
, which now accepts a
concurrency type as its argument, in which case it acts more or less like new
did. There's a new method, create
, that adds a child context without pushing
it onto the stack.
One-off tasks are generally the way to go for most things because they're simple, but sometimes you need so keep state between tasks. So the model for that is to have a persistent thread in the background which will update data periodically, such as a network listener. Core Data's private queue contexts already work that way, but we've added some support to make it easier.
The first is named contexts. Named contexts are available directly off of the
contexts object, like so: cdq.contexts.network
. They may or may not exist in
the context stack. If they're not on the stack, you'll have to save them
directly: cdq.contexts.network.save(nil)
, or use a handy new shortcut: cdq.save(:network)
.
Named contexts are global and permanent.
The second is the on
method, which is a bit of syntactic sugar for calling
performBlock
as required for private contexts.
Putting them together:
cdq.contexts.push(:root)
cdq.contexts.push(:main)
cdq.contexts.push(:private, named: :network)
cdq.contexts.on(:network) do
# stuff
cdq.save(:network, always_wait: true)
cdq.save(:main)
end
As you may have noticed, we've also added symbol shortcuts for concurrency
types, so you can use :private
and :main
in place of the verbose cocoa
constants. Since there should only ever be one, any context of type :main
implicitly names itself main
. There's also a special synonym of :private
called :root
that also implicitly names itself.