Ilya Loshkarev loshkarev.i@gmail.com
SFEDU 2017
Queue | concurrent |
unordered |
---|---|---|
serial |
ordered | |
Task | async |
non blocking |
sync |
blocking |
// Get some queue to put our task into
dispatch_async( dispatch_get_global_queue(
Int(QOS_CLASS_USER_INITIATED.rawValue), 0) )
{
/* do some heavy work */
dispatch_async(dispatch_get_main_queue()) {
/* present results to the user */
}
}
Depricated since iOS 10.0 đź‘Ś
// Get some queue to put our task into
DispatchQueue.global(qos: .userInitiated).async {
/* do some task */
DispatchQueue.main.async {
/* present results to the user */
}
}
Any UI operation should be performed on the main queue
.userInteractive // highest priority
.userInitiated
.utility // default
.background // lowest priority
System prioritizes and schedules queues
according to their QoS attribute
let backgroundQueue = DispatchQueue(label: "com.app.queue",
qos: .background,
attributes: .concurrent)
backgroundQueue.async {
print("Dispatched to background queue")
}
Created queue can be accessed by label
let group = DispatchGroup()
DispatchQueue.global().async(group: group){
/* do stuff */
}
group.wait() // wait for every task in group to complete
group.notify(queue: DispatchQueue.main) {
/* do this after every task in the group is completed */
}
Allow to perform barrier based synchronisation
let semaphore = DispatchSemaphore(value: 10)
semaphore.wait()
/* access some shared data */
semaphore.signal()
Implementation of a traditional counting semaphore
// Performs the receiver’s non-concurrent task
func main()
// The block to execute after the operation’s main task is completed
var completionBlock: (() -> Void)?
An abstract class you use to encapsulate the code and data associated with a single task
let q = OperationQueue()
q.qualityOfService = .utility
q.maxConcurrentOperationCount = 10
q.isSuspended = true
let op = BlockOperation { /* do stuff */ }
op.completionHandler = { /* do other stuff */}
q.addOperation(op: op)
q.isSuspended = false
q.waitUntilAllOperationsAreFinished()
let op = BlockOperation { /* do stuff */ }
op.queuePriority = .high
let dependentOp = BlockOperation { /* do other stuff */ }
dependantOP.addDependency(op) // requires 'op' to be completed
q.addOperation(op)
Operation Queue schedules operations according to their dependencies and priority
var isCancelled: Bool
var isReady: Bool
var isExecuting: Bool
var isFinished: Bool
var isConcurrent: Bool
class FileringOperation: Operation {
var filter: CIFilter!
weak var imageView: UIImageView?
var result: UIImage?
func main() {
if isCanceled { return }
if let source = imageView?.image {
result = filter.apply(to: source)
}
}
}
Key-value coding is a mechanism that allows objects to provide indirect access to their properties
// Returns the value for the property identified by a given key
func value(forKey key: String) -> Any?
// Sets the property of the receiver specified by a given key to a given value
func setValue(_ value: Any?, forKey key: String)
class KVCPerson: NSObject {
@objc var name: String = ""
}
var kvc = KVCPerson()
kvc.setValue("I'm a KVC Object", forKey: #keyPath(KVCPerson.name))
There is no explicit KVC protocol, but any subclass of NSObject
is KVC compliant
class Person {
var name: String
var bestFriend: Person? = nil
}
var han = Person(name: "Han Solo")
var luke = Person(name: "Luke Skywalker")
luke.bestFriend = han
luke[keyPath: \Person.name] = "Master Luke"
luke[keyPath: \Person.bestFriend?.name] // "Han Solo"
Dedicated KeyPath
type allows to reference properties
Works with any Swift type
Would support subscripts in the future
Key-value observing is a mechanism that allows objects to be notified of changes to specified properties of other objects
func observeValue(forKeyPath keyPath: String?, of object: Any?,
change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
func addObserver(_ observer: NSObject, forKeyPath keyPath: String,
options: NSKeyValueObservingOptions = [], context: UnsafeMutableRawPointer?)
func removeObserver(_ observer: NSObject, forKeyPath keyPath: String)
class MyObserver: NSObject {
var myContext = 88888888
override init() {
super.init()
objectToObserve.addObserver(self,
forKeyPath: #keyPath(Observed.propertyName),
options: .new, context: &myContext)
}
deinit {
objectToObserve.removeObserver(self,
forKeyPath: #keyPath(Observed.propertyName),
context: &myContext)
}
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?,
change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if context == &myContext {
if let newValue = change?[.newKey] {
print("Value changed: \(newValue)")
}
} else {
super.observeValue(forKeyPath: keyPath, of: object,
change: change, context: context)
}
}
class Person {
var name: String {
willSet {
print("name will be set to \(newValue)")
}
didSet {
print("name has been changed from \(oldValue)")
}
}
}
let defaultCenter = NotificationCenter.default
name: String // unique name
object: AnyObject? // sender
userInfo: [:]? // additional data
An object distributed through Notification Centers
extension Notification.Name {
static let myNotification = Notification.Name("MyNotification")
static let myNotificationWithPayload = Notification.Name("MyNotificationWithPayload")
}
Any notification should have an unique name within the app
All default notification names are stored in Notification.Name
class
defaultCenter.post(name: .myNotification, object: self)
defaultCenter.post(name: .myNotificationWithPayload,
object: self,
userInfo: ["payload" : myPayload])
defaultCenter.addObserver(self,
selector: #selector(myNotificationHandler),
name: "MyNotification", object: sender)
Calls self.myNotificationHandler
whenever MyNotification
is posted
defaultCenter.removeObserver(self,
name: "MyNotification", object: sender)
Observers should be removed manually on deinit
defaultCenter.addObserver(forName: "MyNotification",
object: sender, queue: nil) {
[weak self] notification in
/* handle notification */
}
Performs closure in specified queue
Strong references to self
in closures
can create reference cycles
DispatchQueue.main.async {
defaultCenter.post(name: "MyNotification", object: nil,
userInfo: ["data":data])
}
Notification centers deliver notifications on the thread
in which the notification was posted