RunloopQueue is a class for running code on a background thread. It works a bit like Grand Central Dispatch, but is instead powered by the magic of run loops!
RunloopQueue works on both macOS and iOS.
queue = RunloopQueue(named: "My Cool Queue")
queue.async {
// This code is running on a background thread.
performAReallyLongOperation()
}To use RunloopQueue, copy the RunloopQueue.swift file to your project. You can also add RunloopQueue+Streams.swift and RunloopQueue+URLConnection.swift if you'd like the Stream and URLConnection helpers.
RunloopQueue is currently being used in our Cascable family of apps, most notably in CascableCore, our SDK for working with WiFi-enabled cameras. CascableCore uses RunloopQueue for managing connections with the cameras it supports, and scheduling messages back and forth. You can find out more about CascableCore in our CascableCore Demo App.
RunloopQueue is not designed to be a replacement for Grand Central Dispatch. In fact, if you're performing the common flow of starting on the main thread, performing some background work then calling back to the main thread at the end of the operation to update UI, you'll need Grand Central Dispatch to get back to the main thread.
queue.async {
// This code is running on a background thread.
performAReallyLongOperation()
DispatchQueue.main.async {
// This code is running on the main thread.
performUIUpdates()
}
}However, RunloopQueue does offer some advantages over Grand Central Dispatch in certain circumstances:
RunloopQueue guarantees that all operations on a given RunloopQueue instance will be executed on the same thread, whether they're performed synchronously or asynchronously. This can be handy if you're interacting with a library that expects thread consistency.
RunloopQueue provides the isRunningOnQueue() method for checking whether or not the code calling that method is running on the given RunloopQueue instance's thread.
Grand Central Dispatch is very unsafe when it comes to synchronous operations, and it's very easy to deadlock your code. Personally, it's so unsafe that I ban its use in any project that I have that sort of influence over.
RunloopQueue, however, is much better in this regard. Thanks to the isRunningOnQueue() method, it can avoid deadlocks when synchronous operations start other synchronous operations:
queue.sync {
queue.sync {
print("Inner sync")
}
print("Outer sync")
}Please note that as in any multithreaded environment, synchronous operations are prone to deadlocking if you're not careful.
If you're working with streams or URLConnection objects, RunloopQueue provides convenience methods for enqueuing such objects on its background thread. This is great if you'd like to process your incoming data in the background:
let input: InputStream = …
let output: OutputStream = …
input.delegate = self
output.delegate = self
// Scheduling the streams on a RunloopQueue will cause their
// delegate methods to be called on a background thread.
queue.schedule(input)
queue.schedule(output)
input.open()
output.open()RunloopQueue is written in Swift 3, but is fully compatible with Objective-C.
self.runloopQueue = [[CBLRunloopQueue alloc] initWithName:@"My Cool Queue"];
[self.runloopQueue async:^{
[self performAReallyLongOperation];
}];
[self.runloopQueue scheduleStream:self.inputStream];For more information, see the LICENSE.md file.