itt 2014 - chris eidhof - practical concurrent programming
TRANSCRIPT
![Page 1: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/1.jpg)
Practical Concurrent ProgrammingChris Eidhof
![Page 2: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/2.jpg)
Merhabā
![Page 3: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/3.jpg)
![Page 4: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/4.jpg)
Why is concurrency hard?
![Page 5: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/5.jpg)
![Page 6: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/6.jpg)
![Page 7: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/7.jpg)
![Page 8: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/8.jpg)
![Page 9: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/9.jpg)
When to use concurrency?
Always use the main thread
![Page 10: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/10.jpg)
When to use concurrency?
— Networking
— Expensive stuff
![Page 11: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/11.jpg)
Decision workflow
1. Measure
2. Change
3. Measure again
![Page 12: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/12.jpg)
How to draw things in the background
![Page 13: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/13.jpg)
![Page 14: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/14.jpg)
Recipe
1. Take drawRect: code
2. Put it in a background thread
3. Update the main thread
![Page 15: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/15.jpg)
The drawRect: code
- (void)drawRect:(CGRect)rect{ CGContextRef ctx = UIGraphicsGetCurrentContext(); // expensive // drawing // code}
![Page 16: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/16.jpg)
Moving it to a different thread
[queue addOperationWithBlock:^{ UIGraphicsBeginImageContextWithOptions(size, NO, 0); // expensive // drawing // code UIImage *i = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // TODO: update the main thread}];
![Page 17: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/17.jpg)
Updating the main thread
[queue addOperationWithBlock:^{ UIGraphicsBeginImageContextWithOptions(size, NO, 0); // expensive // drawing // code UIImage *i = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); [[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.imageView.image = i; }];}];
![Page 18: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/18.jpg)
Deckset filters
![Page 19: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/19.jpg)
![Page 20: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/20.jpg)
[self.queue addOperationWithBlock:^{ CIImage *ciImage = [CIImage imageWithContentsOfURL:sourceURL]; CIFilter *depthOfFieldFilter = [CIFilter filterWithName:@"CIDepthOfField"]; ... CIImage *finalImage = [alphaFilter valueForKey:kCIOutputImageKey]; CGContextRef cgContext = CGBitmapContextCreate(NULL, size.width, size.height, 8, size.width * 4, colorSpace, kCGImageAlphaPremultipliedLast); CIContext *context = [CIContext contextWithCGContext:cgContext options:nil]; CGImageRef outputImage = [context createCGImage:finalImage fromRect:ciImage.extent]; ... CGImageDestinationAddImage(destination, outputImage, nil); CGImageDestinationFinalize(destination);}];
![Page 21: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/21.jpg)
... the shared resource was the GPU!
![Page 22: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/22.jpg)
Solution
NSDictionary *options = @{kCIContextUseSoftwareRenderer: @YES};CIContext *context = [CIContext contextWithCGContext:cgContext options:options];
... and ...
self.queue.maxConcurrentOperationCount = 1;
![Page 23: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/23.jpg)
Solution, part 2
... we removed the complicated filter
![Page 24: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/24.jpg)
![Page 25: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/25.jpg)
How to not load things from the network
dispatch_async(backgroundQueue, ^{ NSData *data = [NSData dataWithContentsOfURL:url]; NSArray *graphItems = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; UIImage* image = [self drawGraph:graphItems]; dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = image; });});
![Page 26: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/26.jpg)
Grand Central Dispatch
![Page 27: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/27.jpg)
![Page 28: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/28.jpg)
![Page 29: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/29.jpg)
Threading is hardGCD moves it to the system-level
![Page 30: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/30.jpg)
GCD Thread PoolMain Thread
High Priority Queue
SerialQueue
ParallelQueue
Serial Queue
MainQueue
SerialQueue
ConcurrentQueue
SerialQueue
Default Priority Queue
Low Priority Queue
Background Priority Queue
Custom Queues
GCD Queues
Threads
![Page 31: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/31.jpg)
— Simpler
— Faster
— Thread-pool management
— Memory-efficient
— Async means: no deadlock!
![Page 32: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/32.jpg)
How to load things from the network
Use NSURLSession
![Page 33: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/33.jpg)
Operation Queues
![Page 34: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/34.jpg)
dispatch_async(backgroundQueue, ^{ NSArray *graphData = [self generateDataPointsForGraph]; UIImage* image = [self drawGraph:graphData]; dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = image; });});
![Page 35: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/35.jpg)
Step 1: Use NSOperation
NSOperationQueue* drawingQueue = [[NSOperationQueue alloc] init];[drawingQueue addOperationWithBlock:^{ NSArray *graphData = [self generateDataPointsForGraph]; UIImage* image = [self drawGraph:graphData]; [[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.imageView.image = image; }];}];
How to cancel this?
![Page 36: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/36.jpg)
Step 2: Use NSBlockOperation
NSBlockOperation* drawingOperation = [NSBlockOperation blockOperationWithBlock:^{ NSArray *graphData = [self generateDataPointsForGraph]; UIImage* image = [self drawGraph:graphData]; [[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.imageView.image = image; }]; }];[drawingQueue addOperation:drawingOperation];
![Page 37: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/37.jpg)
Step 3: Pull out the completion handler
NSOperation* drawingOperation = [NSBlockOperation blockOperationWithBlock:^{ NSArray *graphData = [self generateDataPointsForGraph]; self.image = [self drawGraph:graphData];}];drawingOperation.completionBlock = ^{ [[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.imageView.image = self.image; }];};
![Page 38: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/38.jpg)
Step 4: Custom NSOperation subclass
NSData *data = [self generateDataPointsForGraph];NSOperation* drawingOperation = [DrawingOperation drawingOperationWithData:data];drawingOperation.completionBlock = ^{ [[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.imageView.image = image; }];};
![Page 39: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/39.jpg)
How to efficiently import data into Core Data
![Page 40: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/40.jpg)
![Page 41: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/41.jpg)
Efficiently Importing Data
— Implement Find-or-Create Efficiently
— Reduce Peak Memory Footprint
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html
![Page 42: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/42.jpg)
Importing employees
for (WebserviceEmployee *webserviceEmployee) { NSNumber *identifier = webserviceEmployee.identifier; Employee *employee = [self findEmployeeWithIdentifier:identifier]; if (employee == nil) { employee = [self createEmployeWithIdentifier:identifier]; } // Update data}
![Page 43: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/43.jpg)
Importing more efficiently
NSMutableArray *identifiers = [NSMutableArray array];for (WebserviceEmployee *webserviceEmployee) { NSNumber *identifier = webserviceEmployee.identifier; [identifiers addObject:identifier];}NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(employeeID IN %@)", identifiers];
![Page 44: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/44.jpg)
Import in batchesUse a separate context
![Page 45: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/45.jpg)
Normal Core Data Stack
![Page 46: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/46.jpg)
NSManagedObjectContextNSManagedObjectContext NSManagedObjectContext
SQLite
NSPersistentStore
NSPersistentStoreCoordinator
Double MOC Stack
![Page 47: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/47.jpg)
Small imports
NSManagedObjectContext* context = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];context.persistentStoreCoordinator = self.ptStoreCoordinator;[self.context performBlock:^{ [self import];}];
![Page 48: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/48.jpg)
[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:nil usingBlock:^(NSNotification* note){ NSManagedObjectContext *moc = self.mainMOC; if (note.object != moc) { [moc performBlock:^(){ [moc mergeChangesFromContextDidSaveNotification:note]; }]; };}];
![Page 49: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/49.jpg)
NSManagedObjectContextNSManagedObjectContext NSManagedObjectContext!
SQLite
NSPersistentStore
NSPersistentStoreCoordinator!
!
!
Double MOC Stack
![Page 50: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/50.jpg)
You might want to consider using a different concurrency style, and this time you have two persistent store coordinators, two almost completely separate Core Data stacks.
Source: http://asciiwwdc.com/2013/sessions/211
![Page 51: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/51.jpg)
SQLite!
NSPersistentStore
NSPersistentStoreCoordinator
NSManagedObjectContext
NSPersistentStore
NSPersistentStoreCoordinator
NSManagedObjectContext!
!
!NSPersistentStore
NSPersistentStoreCoordinator
NSManagedObjectContext
NSPersistentStore
NSPersistentStoreCoordinator
NSManagedObjectContext!
!
!
![Page 52: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/52.jpg)
SQLite!
NSPersistentStore
NSPersistentStoreCoordinator
NSManagedObjectContext
NSPersistentStore
NSPersistentStoreCoordinator
NSManagedObjectContext!
!
!NSPersistentStore
NSPersistentStoreCoordinator
NSManagedObjectContext
NSPersistentStore
NSPersistentStoreCoordinator
NSManagedObjectContext!
!
!
NSManagedObjectContextNSManagedObjectContext NSManagedObjectContext!
SQLite
NSPersistentStore
NSPersistentStoreCoordinator!
!
!
![Page 53: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/53.jpg)
Importing Recap
Didn't turn out to be a problem: we shipped the sqlite file for the initial import.
![Page 54: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/54.jpg)
How to make objects play well when concurrent
![Page 55: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/55.jpg)
@interface Account : NSObject
@property (nonatomic) double balance;
- (void)transfer:(double)euros to:(Account*)other;
@end
![Page 56: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/56.jpg)
- (void)transfer:(double)euros to:(Account*)other{ self.balance = self.balance - euros; other.balance = other.balance + euros;}
What happens if two methods call this method at the same time? From different threads?
![Page 57: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/57.jpg)
The same code, how the compiler sees it
- (void)transfer:(double)euros to:(Account*)other{ double currentBalance = self.balance; self.balance = currentBalance - euros; double otherBalance = other.balance; other.balance = otherBalance + euros;}
![Page 58: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/58.jpg)
a b [a.transfer:20 to:b] [a.transfer:30 to:b]
100 0
currentBalance = 100
currentBalance = 100
100 0
a.balance = 100 - 20
80 0
b.balance = b.balance + 20
80 20
a.balance = 100 - 30
70 20
![Page 59: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/59.jpg)
a b [a.transfer:20 to:b] [a.transfer:30 to:b]
100 0
currentBalance = 100
currentBalance = 100
100 0
a.balance = 100 - 20
80 0
b.balance = b.balance + 20
80 20
a.balance = 100 - 30
70 20
![Page 60: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/60.jpg)
- (void)transfer:(double)euros to:(Account*)other{ @synchronized(self) { self.balance = self.balance - euros; other.balance = other.balance + euros; }}
![Page 61: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/61.jpg)
- (void)transfer:(double)euros to:(Account*)other{ @synchronized(self) { @synchronized(other) { self.balance = self.balance - euros; other.balance = other.balance + euros; }
}}
Problem: deadlock.
![Page 62: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/62.jpg)
- (void)transfer:(double)euros to:(Account*)other{ @synchronized(self.class) { self.balance = self.balance - euros; other.balance = other.balance + euros; }}
![Page 63: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/63.jpg)
Solution: move concurrency to a different level
![Page 64: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/64.jpg)
Do it the GCD way
Account* account = [Account new];Account* other = [Account new];dispatch_queue_t accountOperations = dispatch_queue_create("accounting", DISPATCH_QUEUE_SERIAL);dispatch_async(accountOperations, ^{ [account transfer:200 to:other];});
dispatch_async will never block.
![Page 65: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/65.jpg)
![Page 66: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/66.jpg)
There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first
method is far more difficult.
— Tony Hoare
![Page 67: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/67.jpg)
Thanks
— @chriseidhof
— http://www.objc.io
— http://www.uikonf.com
— http://www.decksetapp.com
![Page 68: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/68.jpg)
Resources
— Concurrency Programming Guide
— Threading Programming Guide
— NSOperationQueue class reference
— http://www.objc.io/issue-2/
— http://www.opensource.apple.com/source/objc4/objc4-551.1/runtime/objc-sync.mm
— http://googlemac.blogspot.de/2006/10/synchronized-swimming.html
— WWDC12 #211: Concurrent User Interfaces on iOS
![Page 69: ITT 2014 - Chris Eidhof - Practical Concurrent Programming](https://reader033.vdocument.in/reader033/viewer/2022042907/587717e11a28ab4c1d8b5a91/html5/thumbnails/69.jpg)
Icons are from the Noun Project:
— Coffee Maker by Maureen Placente
— Coffee by Julia Soderberg
— Railroad Crossing by Edward Boatman
— Database by Stefan Parnarov
— Drawing by Daniel Shannon
— Hammer by Alex AS
— Lock by P.J. Onori
— Photoshop by Joe Harrison
— Register by Wilson Joseph