Eki lets you manage easily concurrency in your apps. This framework makes Grand Central Dispatch easy and fun to use.
- iOS 8.0+ / Mac OS X 10.10+
- Xcode 6.3
Internally GCD manages a pool of threads which process dispatch queues and invoke blocks submitted to them.
Main
UserInteractive
UserInitiated
Default
Utility
Background
The queues are ordered in descending priority order.
You access them like so:
Queue.Background
You dispatch a block on queue asynchronously by using async
or synchronously by using sync
:
// Asynchronously
Queue.Utility.async {
...
}
// Or asynchronously using the operator shortcut
Queue.Utility <<< {
...
}
// Synchronously
Queue.Utility.sync { // Eki will prevent deadlock if you submit a sync on the current queue
...
}
You can send multiple blocks to a queue:
Queue.Utility.async {
// Block 1
}.async {
// Block 2
}
// Or by submitting an array of blocks:
let blocks = [{
// Block 1
}, {
// Block 2
}]
Queue.Utility.async(blocks)
Create your own queue (serial or concurrent):
let queue = Queue(name:"QueueName", kind:.Concurrent)
queue.async{
...
}
Dispatch a block asynchronously with barrier:
let queue:Queue = Queue(name:"QueueName", type:.Concurrent)
...
queue.barrierAsync { // Or operator |<|
// This block will be executed on the queue only after all previous submitted blocks have been executed
}.barrierAsync {
// This block will be executed only after the previous barrier block have completed
}
Queue.Background.after(2) {
// Do some stuff on Background after 2 seconds
}
Queue.Background.iterate(4) { i in
// Do some stuff on Background 4 times
}
Queue.current // Get current queue
Queue.Background.isCurrent // Check if background is current queue
Take notice that will work only on Custom Queues created with the designed initializer Queue(name:String, kind:Queue.Custom.Kind)
, the Main queue and Global queues.
A task represents a block to be dispatched on a queue.
let t = Task(queue:.Utility) {
...
}
// Or
let t = Queue.Utility + {
...
}
t.async() // Dispatch asynchronously
group.async(t) // Dispatch on a group
let tasks:[Task] = ...
g.async(tasks) // Tasks dispatched on a group.
A task can be chained with a block
or an another Task
t.chain {
// Executed after t on same queue
}.chain(Task(queue:.Main) {
// Executed after previous block on the main queue
})
t.async()
// Or chain directly after async and use the operator shortcut
t.async() <> {
// Executed after t on same queue
} <> Queue.Main + {
// Executed after previous block on the main queue
}
A group allows to associate multiple blocks to be dispatched asynchronously.
let g = Group(queue:.Utility) // By default the group queue is Background
g.async {
// Block dispatched on the group's queue.
} <<< {
// Block dispatched on the group's queue using the operator.
} <<< Task(queue:.Main) {
// Block dispatched on the Main queue (see Task).
}
let blocks:[()-> Void] = ...
g.async(blocks) // Blocks dispatched on the group's queue.
There is two ways to track group's blocks execution:
g.notify {
// Block executed on the group queue when blocks previously dispatched on the group have been executed.
}
g.notify(Queue.Main + {
// Block executed on the Main queue when blocks previously dispatched on the group have been executed.
})
g.wait() // Wait on the current process the group's blocks execution.
Execute a block once and only once.
let once = OnceDispatcher() // Store it somewhere
...
once {
// Executed only one time
}
A timer allows to schedule a block on a specified queue with an interval or a date.
let timer = Timer.scheduleWithInterval(2, onQueue: .Background) {
// Do some stuff on Background after 2 seconds
}
// Equivalent to:
let timer = Timer(queue: .Background, interval: 2)
timer.handler {
// Do some stuff on Background after 2 seconds
}
timer.start() // Timers are paused at Initialization
A timer can be paused or stopped.
timer.pause()
timer.start()
timer.stop()
A timer can be repeated, use a date...
let date: NSDate = ...
let timer = Timer(queue: .Background, date: date)
timer.repeatInterval = 4
timer.tolerance = 1 // Add some tolerance
timer.handler {
// Do some stuff on Background on specified date and after every 4 seconds approximately
}
timer.start()
There is three kinds of semaphore:
Kind | Initial Resource(s) |
---|---|
Binary | 1 |
Barrier | 0 |
Counting(resource:UInt16) | custom |
Initialize a semaphore:
let sem = Semaphore(.Binary)
let customSem = Semaphore(resource:5)
You can decrement/increment semaphore's resource by using wait/signal
methods:
sem.wait()
// Do some stuff when a resource is available
sem.signal()
// Or
sem--
...
sem++
Or by using the perform
convenient method with a closure:
sem.perform {
// Do some stuff when a resource is available
}
// Or
sem <<< {
...
}
A mutex is essentially the same thing as a binary semaphore except that only the block that locked the resource is supposed to unlock it.
let m = Mutex()
...
m.sync {
// Do some stuff when a mutext is available
}
// Or
m <<< {
...
}
LockedObject
is convenient class to lock access to an object with an internal mutext.
let myobj = MyObject()
let l = LockedObject(myobj)
...
l.access { obj in
// Only one process at a time will access the locked object
}
// Or
l <<< { obj in
...
}
Use with cocoapods
Add pod 'Eki'
to your Podfile
and run pod install
.