Slide 1

Slide 1 text

Multithreading on iOS

Slide 2

Slide 2 text

Why Multithreading?

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

How to do concurrent work?

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

How to do concurrent work? • Serial and concurrent queues • QoS classes • Main queue, default queues and custom queues Grand Central Dispatch

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Living in threaded environments

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

Making use of queues

Slide 16

Slide 16 text

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 *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

Slide 17

Slide 17 text

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 *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

Slide 18

Slide 18 text

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 *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

Slide 19

Slide 19 text

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 *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

Slide 20

Slide 20 text

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 *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

Slide 21

Slide 21 text

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 *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

Slide 22

Slide 22 text

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 *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

Slide 23

Slide 23 text

Making use of queues @interface Event : NSObject + (NSProgress *)getWithCoordinate:(CLLocationCoordinate2D)coordinate
 completionHandler:(void(^)(
 NSArray *events,
 NSError *error) )completionHandler; @end Data Source

Slide 24

Slide 24 text

Being thread safe

Slide 25

Slide 25 text

Being thread safe @interface Event : NSObject @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

Slide 26

Slide 26 text

Being thread safe @interface Event : NSObject @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

Slide 27

Slide 27 text

Being thread safe @interface Event : NSObject @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

Slide 28

Slide 28 text

Being thread safe @interface Event : NSObject @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

Slide 29

Slide 29 text

Being thread safe @interface Event : NSObject @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

Slide 30

Slide 30 text

Being thread safe @interface Event : NSObject @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

Slide 31

Slide 31 text

Being thread safe @interface Event : NSObject @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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Convenience

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

Thank you Michael Ochs @_mochs http://ios-coding.com