Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Multithreading on iOS

Multithreading on iOS

This talk gives an overview why and when it is useful to consider to do stuff concurrently in an iOS application. It then goes into detail about some of the concepts and useful patterns that exist in Grand Central Dispatch.

Michael Ochs

August 27, 2015
Tweet

More Decks by Michael Ochs

Other Decks in Programming

Transcript

  1. Why Multithreading? • Keep the main thread free for UI

    rendering • Use all available CPU cores • Save energy and reduce battery consumption • Prioritise tasks to enable the system to optimise the workload
  2. How to do concurrent work? • NSThread • NSRunLoop •

    NSOperation / NSOperationQueue • Grand Central Dispatch Available concepts
  3. How to do concurrent work? • NSThread • NSRunLoop •

    NSOperation / NSOperationQueue • Grand Central Dispatch Available concepts
  4. How to do concurrent work? • Queues are an abstraction

    on top of threads • Queues can have parent queues • Multiple queues can run on a single thread • A single queue can run on multiple threads • Multiple queues use a thread pool Threads vs. Queues
  5. How to do concurrent work? • Configurable number of parallel

    executions • Cancellable • Dependencies, priorities, QoS classes • Based on GCD • Main queue and custom queues NSOperation / NSOperationQueue
  6. How to do concurrent work? • Serial and concurrent queues

    • QoS classes • Main queue, default queues and custom queues Grand Central Dispatch
  7. How to do concurrent work? • NSOperationQueue can control maximum

    parallel execution • GCD has default queues • GCD has groups and semaphores NSOperationQueue vs. Grand Central Dispatch
  8. How to do concurrent work? • NSOperationQueue is more convenient

    • GCD has all the fancy shit • GCD is much more than just getting stuff off the main queue NSOperationQueue vs. Grand Central Dispatch
  9. Living in threaded environments • Make your models immutable •

    Have a single source of truth • Make your data source thread safe • Never rely on messages arriving on a certain queue unless explicitly documented • Explicitly document if you rely on being run on a certain queue Models
  10. Living in threaded environments • Design your APIs for asynchronous

    use • Ensure completion handler are called on a well defined queue • Never use mutable data types on public APIs • Ensure mutating methods are thread safe API Design
  11. Making use of queues __weak typeof(self) weakSelf = self; qos_class

    qosClass = QOS_CLASS_USER_INITIATED; dispatch_queue_t queue = dispatch_get_global_queue(qosClass, 0); dispatch_async(queue, ^{ NSArray<Location *> *locations = // do some heavy work here dispatch_async(dispatch_get_main_queue(), ^{ typeof(weakSelf) self = weakSelf; [self.mapView addAnnotations:locations]; [self.mapView showAnnotations:locations animated:YES]; }); }); User initiated actions
  12. Making use of queues __weak typeof(self) weakSelf = self; qos_class

    qosClass = QOS_CLASS_USER_INITIATED; dispatch_queue_t queue = dispatch_get_global_queue(qosClass, 0); dispatch_async(queue, ^{ NSArray<Location *> *locations = // do some heavy work here dispatch_async(dispatch_get_main_queue(), ^{ typeof(weakSelf) self = weakSelf; [self.mapView addAnnotations:locations]; [self.mapView showAnnotations:locations animated:YES]; }); }); User initiated actions
  13. Making use of queues __weak typeof(self) weakSelf = self; qos_class

    qosClass = QOS_CLASS_USER_INITIATED; dispatch_queue_t queue = dispatch_get_global_queue(qosClass, 0); dispatch_async(queue, ^{ NSArray<Location *> *locations = // do some heavy work here dispatch_async(dispatch_get_main_queue(), ^{ typeof(weakSelf) self = weakSelf; [self.mapView addAnnotations:locations]; [self.mapView showAnnotations:locations animated:YES]; }); }); User initiated actions
  14. Making use of queues __weak typeof(self) weakSelf = self; qos_class

    qosClass = QOS_CLASS_USER_INITIATED; dispatch_queue_t queue = dispatch_get_global_queue(qosClass, 0); dispatch_async(queue, ^{ NSArray<Location *> *locations = // do some heavy work here dispatch_async(dispatch_get_main_queue(), ^{ typeof(weakSelf) self = weakSelf; [self.mapView addAnnotations:locations]; [self.mapView showAnnotations:locations animated:YES]; }); }); User initiated actions
  15. Making use of queues __weak typeof(self) weakSelf = self; qos_class

    qosClass = QOS_CLASS_USER_INITIATED; dispatch_queue_t queue = dispatch_get_global_queue(qosClass, 0); dispatch_async(queue, ^{ NSArray<Location *> *locations = // do some heavy work here dispatch_async(dispatch_get_main_queue(), ^{ typeof(weakSelf) self = weakSelf; [self.mapView addAnnotations:locations]; [self.mapView showAnnotations:locations animated:YES]; }); }); User initiated actions
  16. Making use of queues __weak typeof(self) weakSelf = self; qos_class

    qosClass = QOS_CLASS_USER_INITIATED; dispatch_queue_t queue = dispatch_get_global_queue(qosClass, 0); dispatch_async(queue, ^{ NSArray<Location *> *locations = // do some heavy work here dispatch_async(dispatch_get_main_queue(), ^{ typeof(weakSelf) self = weakSelf; [self.mapView addAnnotations:locations]; [self.mapView showAnnotations:locations animated:YES]; }); }); User initiated actions
  17. Making use of queues __weak typeof(self) weakSelf = self; qos_class

    qosClass = QOS_CLASS_USER_INITIATED; dispatch_queue_t queue = dispatch_get_global_queue(qosClass, 0); dispatch_async(queue, ^{ NSArray<Location *> *locations = // do some heavy work here dispatch_async(dispatch_get_main_queue(), ^{ typeof(weakSelf) self = weakSelf; [self.mapView addAnnotations:locations]; [self.mapView showAnnotations:locations animated:YES]; }); }); User initiated actions
  18. Making use of queues @interface Event : NSObject <NSCopying, NSMutableCopying,

    NSCoding> + (NSProgress *)getWithCoordinate:(CLLocationCoordinate2D)coordinate
 completionHandler:(void(^)(
 NSArray<Event *> *events,
 NSError *error) )completionHandler; @end Data Source
  19. Being thread safe @interface Event : NSObject <NSCopying, NSMutableCopying, NSCoding>

    @property (nonatomic, strong, readonly) NSNumber *identifier; @property (nonatomic, strong, readonly) NSDate *startDate; @property (nonatomic, strong, readonly) NSString *name; @end @interface MutableEvent : Event @property (nonatomic, strong, readwrite) NSNumber *identifier; @property (nonatomic, strong, readwrite) NSDate *startDate; @property (nonatomic, strong, readwrite) NSString *name; @end Immutability
  20. Being thread safe @interface Event : NSObject <NSCopying, NSMutableCopying, NSCoding>

    @property (nonatomic, strong, readonly) NSNumber *identifier; @property (nonatomic, strong, readonly) NSDate *startDate; @property (nonatomic, strong, readonly) NSString *name; @end @interface MutableEvent : Event @property (nonatomic, strong, readwrite) NSNumber *identifier; @property (nonatomic, strong, readwrite) NSDate *startDate; @property (nonatomic, strong, readwrite) NSString *name; @end Immutability
  21. Being thread safe @interface Event : NSObject <NSCopying, NSMutableCopying, NSCoding>

    @property (nonatomic, strong, readonly) NSNumber *identifier; @property (nonatomic, strong, readonly) NSDate *startDate; @property (nonatomic, strong, readonly) NSString *name; @end @interface MutableEvent : Event @property (nonatomic, strong, readwrite) NSNumber *identifier; @property (nonatomic, strong, readwrite) NSDate *startDate; @property (nonatomic, strong, readwrite) NSString *name; @end Immutability
  22. Being thread safe @interface Event : NSObject <NSCopying, NSMutableCopying, NSCoding>

    @property (nonatomic, strong, readonly) NSNumber *identifier; @property (nonatomic, strong, readonly) NSDate *startDate; @property (nonatomic, strong, readonly) NSString *name; @end @interface MutableEvent : Event @property (nonatomic, strong, readwrite) NSNumber *identifier; @property (nonatomic, strong, readwrite) NSDate *startDate; @property (nonatomic, strong, readwrite) NSString *name; @end Immutability
  23. Being thread safe @interface Event : NSObject <NSCopying, NSMutableCopying, NSCoding>

    @property (nonatomic, strong, readonly) NSNumber *identifier; @property (nonatomic, strong, readonly) NSDate *startDate; @property (nonatomic, strong, readonly) NSString *name; @end @interface MutableEvent : Event @property (nonatomic, strong, readwrite) NSNumber *identifier; @property (nonatomic, strong, readwrite) NSDate *startDate; @property (nonatomic, strong, readwrite) NSString *name; @end Immutability
  24. Being thread safe @interface Event : NSObject <NSCopying, NSMutableCopying, NSCoding>

    @property (nonatomic, strong, readonly) NSNumber *identifier; @property (nonatomic, strong, readonly) NSDate *startDate; @property (nonatomic, strong, readonly) NSString *name; @end @interface MutableEvent : Event @property (nonatomic, strong, readwrite) NSNumber *identifier; @property (nonatomic, strong, readwrite) NSDate *startDate; @property (nonatomic, strong, readwrite) NSString *name; @end Immutability
  25. Being thread safe @interface Event : NSObject <NSCopying, NSMutableCopying, NSCoding>

    @property (nonatomic, strong, readonly) NSNumber *identifier; @property (nonatomic, strong, readonly) NSDate *startDate; @property (nonatomic, strong, readonly) NSString *name; @end @interface MutableEvent : Event @property (nonatomic, strong, readwrite) NSNumber *identifier; @property (nonatomic, strong, readwrite) NSDate *startDate; @property (nonatomic, strong, readwrite) NSString *name; @end Immutability
  26. Being thread safe @interface FavoriteEventsController : NSObject @property (nonatomic, strong,

    readonly) NSArray<Event *> *events; - (void)addEvent:(Event *)event; - (void)removeEvent:(Event *)event; @end Thread safety
  27. Being thread safe @interface FavoriteEventsController : NSObject @property (nonatomic, strong,

    readonly) NSArray<Event *> *events; - (void)addEvent:(Event *)event; - (void)removeEvent:(Event *)event; @end Thread safety
  28. Being thread safe @interface FavoriteEventsController : NSObject @property (nonatomic, strong,

    readonly) NSArray<Event *> *events; - (void)addEvent:(Event *)event; - (void)removeEvent:(Event *)event; @end Thread safety
  29. Being thread safe - (instancetype)init { self = [super init];

    if (self) { dispatch_queue_attr_t attributes = DISPATCH_QUEUE_CONCURRENT; if (&dispatch_queue_attr_make_with_qos_class != NULL) { attr = dispatch_queue_attr_make_with_qos_class(attributes,
 QOS_CLASS_BACKGROUND, -1); } dispatch_queue_t workerQueue = dispatch_queue_create("fav", attr); _workerQueue = workerQueue; _events = @[]; } return self; } Thread safety - FavoriteEventsController
  30. Being thread safe - (instancetype)init { self = [super init];

    if (self) { dispatch_queue_attr_t attributes = DISPATCH_QUEUE_CONCURRENT; if (&dispatch_queue_attr_make_with_qos_class != NULL) { attr = dispatch_queue_attr_make_with_qos_class(attributes,
 QOS_CLASS_BACKGROUND, -1); } dispatch_queue_t workerQueue = dispatch_queue_create("fav", attr); _workerQueue = workerQueue; _events = @[]; } return self; } Thread safety - FavoriteEventsController
  31. Being thread safe - (instancetype)init { self = [super init];

    if (self) { dispatch_queue_attr_t attributes = DISPATCH_QUEUE_CONCURRENT; if (&dispatch_queue_attr_make_with_qos_class != NULL) { attr = dispatch_queue_attr_make_with_qos_class(attributes,
 QOS_CLASS_BACKGROUND, -1); } dispatch_queue_t workerQueue = dispatch_queue_create("fav", attr); _workerQueue = workerQueue; _events = @[]; } return self; } Thread safety - FavoriteEventsController
  32. Being thread safe - (instancetype)init { self = [super init];

    if (self) { dispatch_queue_attr_t attributes = DISPATCH_QUEUE_CONCURRENT; if (&dispatch_queue_attr_make_with_qos_class != NULL) { attr = dispatch_queue_attr_make_with_qos_class(attributes,
 QOS_CLASS_BACKGROUND, -1); } dispatch_queue_t workerQueue = dispatch_queue_create("fav", attr); _workerQueue = workerQueue; _events = @[]; } return self; } Thread safety - FavoriteEventsController
  33. Being thread safe - (instancetype)init { self = [super init];

    if (self) { dispatch_queue_attr_t attributes = DISPATCH_QUEUE_CONCURRENT; if (&dispatch_queue_attr_make_with_qos_class != NULL) { attr = dispatch_queue_attr_make_with_qos_class(attributes,
 QOS_CLASS_BACKGROUND, -1); } dispatch_queue_t workerQueue = dispatch_queue_create("fav", attr); _workerQueue = workerQueue; _events = @[]; } return self; } Thread safety - FavoriteEventsController
  34. Being thread safe - (NSArray<Event *> *)events { __block NSArray

    *events; dispatch_sync(self.workerQueue, ^{ events = _events; }); return events; } - (void)setEvents:(NSArray<Event *> *)events { dispatch_barrier_async(self.workerQueue, ^{ _events = events; }); } Thread safety - FavoriteEventsController
  35. Being thread safe - (NSArray<Event *> *)events { __block NSArray

    *events; dispatch_sync(self.workerQueue, ^{ events = _events; }); return events; } - (void)setEvents:(NSArray<Event *> *)events { dispatch_barrier_async(self.workerQueue, ^{ _events = events; }); } Thread safety - FavoriteEventsController
  36. Being thread safe - (NSArray<Event *> *)events { __block NSArray

    *events; dispatch_sync(self.workerQueue, ^{ events = _events; }); return events; } - (void)setEvents:(NSArray<Event *> *)events { dispatch_barrier_async(self.workerQueue, ^{ _events = events; }); } Thread safety - FavoriteEventsController
  37. Being thread safe - (void)addEvent:(Event *)event { dispatch_barrier_async(self.workerQueue, ^{ _events

    = [_events arrayByAddingObject:event]; }); } - (void)removeEvent:(Event *)event { dispatch_barrier_async(self.workerQueue, ^{ NSPredicate *p = [NSPredicate
 predicateWithFormat:@"NOT (SELF == %@)", event]; _events = [_events filteredArrayUsingPredicate:p]; }); } Thread safety - FavoriteEventsController
  38. Convenience dispatch_group_t fetchGroup = dispatch_group_create(); for (int i = 0;

    i < 10; i++) { dispatch_group_enter(fetchGroup); [Fetcher doWithCompletionHandler:^(BOOL success) { // Handle completion dispatch_group_leave(fetchGroup); }]; } dispatch_group_wait(fetchGroup, DISPATCH_TIME_FOREVER); // Do stuff after all completions finished Dispatch groups - Synchronously
  39. Convenience dispatch_group_t fetchGroup = dispatch_group_create(); for (int i = 0;

    i < 10; i++) { dispatch_group_enter(fetchGroup); [Fetcher doWithCompletionHandler:^(BOOL success) { // Handle completion dispatch_group_leave(fetchGroup); }]; } dispatch_group_notify(fetchGroup, dispatch_get_main_queue(), ^{ // Do stuff after all completions finished }); Dispatch groups - Asynchronously