Blocks & GCD
- 1. ^{ }
blocks
and
Raphael Sebbe
creaceed.com
cocoaheads.be @rsebbe
- 2. Ways to Get More
• We can’t get more GHz out of silicon
• Parallelism is the solution
• Nowadays, multi-core systems is the rule
• On it’s way to mobile devices (iPad2)
• Specialized hardware, like GPU, too
• Already there on mobile
- 3. GPU
Core
SIMD
Multi-
Cloud
Farms/
Core Image
OpenCL
GLSL
Data-driven
Accelerate
Threads
Grand
Central
Dispatch
Load
Cron
Balancing
Task-driven
OpenMP
- 4. Overview
• Blocks & GCD are 2 different beasts
• New language feature
• Efficiently define tasks, and execute them
• They complement each other nicely
- 5. Blocks
• Concept of closure from functional
programming
Lisp, Caml, Haskell,
• Blocks Javascript, etc.
• are an extension of C language
• store a piece of executable code
• capture the execution context
• are objects (Obj-C or C-based API)
- 6. Definition of a Block
// Simple one
void (^myblock)() = ^{
! printf(“Hellon”);
};
myblock();
// With one arg
void (^myblock)(int) = ^(int arg){
! printf(“Hello arg=%dn”, arg);
};
myblock(1);
// With a return value
int (^myblock)(int) = ^(int arg){
! printf(“Hello arg=%dn”, arg);
! return arg + 1; ~ anonymous
}; function pointer
int r = myblock(1);
- 7. Typedef’ing a Block
// Typedef of a block type
typedef void (^MyBlockType)(NSURL *assetURL, NSError
*error);
MyBlockType myBlock = ^(NSURL *assetURL, NSError
*error) {
! // Some code
};
// Syntax of untyped block as Obj-C method argument
- (void)addPlayersToMatch:(GKMatch *)match
matchRequest:(GKMatchRequest *)matchRequest
completionHandler:(void (^)(NSError *))handler
- 8. Execution Context (1/3)
• Definition of a block captures the
execution context
• Variable of standard C types are copied
int a = 3;
// Context is captured at it’s current state
void (^myblock)() = ^{
! printf(“Hello a=%dn”, a);
};
a = 5;
myblock(); // outputs “Hello a=3”
- 9. Execution Context (2/3)
• Captured objects are retained
id dict = [[NSDictionary alloc] init];
// Referenced objects are retained
void (^myblock)() = ^{
! NSLog(“Hello dict=%@n”, dict);
};
[dict release];
myblock(); // outputs “Hello dict=....” Remark: For
referenced ivar, it’s
self that gets
retained. Possible
retain cycles.
- 10. Execution Context (3/3)
• Shared (R/W) variables with __block
__block int a = 3;
// Context is captured at it’s current state
void (^myblock)() = ^{
! a += 1;
! printf(“Hello a=%dn”, a);
};
a = 5;
Remark:
myblock(); // outputs “Hello a=6” __block objects
(id, etc.) are not
retained
- 11. Block Storage
• When defined, blocks are stack objects,
only valid in the definition scope
• Must be copied to heap if used outside of
creation context
if (x) { if (x) {
! block = ^{ printf("truen");}; ! block = ^{ printf("truen");};
} else { ! block = [block copy];
! block = ^{ printf("falsen");}; } else {
} ! block = ^{ printf("falsen");};
block(); // unsafe!!! ! block = [block copy];Remark:
}
block(); // safe!!!
If your method
[block release]; takes a block as
arg., always copy
it.
- 12. Blocks Advantages
• Idea: invoke this “block of code” later.
• Callback API are naturally expressed
• Good replacement to your many didEnd:
methods
• Task queueing / parallelism much easier
• See GCD
- 13. Grand Central Dispatch
• Need for parallelism that scales
• Easy to program for wide adoption
• Parallelism examples
• iTunes - Coverflow, downloads...
• iPhoto - import, uploads, preview
• Twitter Profile Pictures, etc.
- 14. Grand Central Dispatch
• Multi-core needs threads
• Say you have a 4-core machine, you need 4
threads for maximum throughput.
• Well... No.
• This is a pre-emptive world
- 15. Threads (1/2)
• Optimal number of threads for your app
• Number of (virtual) cores
• Load from other apps
• Thread state (low speed I/O)
• This continuously changes
• Consequences: unused or overused cores
- 16. Threads (2/2)
• 3 problems with threads:
• Too low-level, you shouldn’t waste your time
at this (when to create/destroy them)
• You generally don’t have easy access to
global system info
• Thread programming is hard (sync
primitives, etc.)
- 19. GCD Concept
• Tasks in Queues Out
In
In Out
Core Q’s are
lightweight, have
Core as many as you
want
- 20. Why Q’s
• Goal: Best use of cores
• Let the system create/destroy threads for you
• Write less code: wide adoption
• Better overall user experience (responsiveness)
• Technical: Less contention
• Fewer locks
• Scales much better with
• number of tasks
• number of cores
- 21. Queue Types
• Serial queues: one task at a time Serial Q’s are
• User queues themselves sync
primitives
• Main queue
• Concurrent queues: multiple tasks
• 3 priority levels Only the
global one on
10.6 / iOS4
- 22. Queues
dispatch_queue_t queue;
// Main (serial) queue
queue = dispatch_get_main_queue();
// User (serial) queue
queue = dispatch_queue_create(“com.mycompany.qname”,
! NULL);
// Global (concurrent) queue, with priority
queue = dispatch_get_global_queue
! (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- 23. Adding Tasks
// Synchronous, blocks until code is executed
dispatch_sync(queue, ^{
! // task code
});
dispatch_apply(count, queue, ^(size_t i){
! // process ith
});
// Asynchronous, returns immediately to caller
dispatch_async(queue, ^{
! // task code API exists for
});
function pointers
dispatch_after(when, queue, ^{ too.
! // task code
});
- 25. Adding Operations
// Synchronous, blocks until code is executed
NSOperationQueue *queue = [[NSOQ alloc] init];
!
NSBlockOperation *op1 = [NSBlockOperation
! blockOperationWithBlock:^{
! ! // some code! !
! }];
NSBlockOperation *op2 = [NSBlockOperation
! blockOperationWithBlock:^{ Predates
! ! // some code!
! }]; GCD, has some
! odd APIs. Now built
[op2 addDependency:op1];
!
on top of it.
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperationWithBlock:^{
! // some code! !
You can set
}]; the max concurrent
ops.
- 26. APIs using Blocks (1/2)
// Collections (NSDictionary / NSArray / NSSet)
- (void)enumerateKeysAndObjectsWithOptions:
(NSEnumerationOptions)opts usingBlock:(void (^)(id
key, id obj, BOOL *stop))block
- (NSSet *)keysOfEntriesPassingTest:(BOOL (^)(id
key, id obj, BOOL *stop))predicate
Patterns for collections:
- (NSArray *)keysSortedByValueUsingComparator:
(NSComparator)cmptr
enumeration, mutation,
tests, comparison
// NSRegularExpression
- (void)enumerateMatchesInString:(NSString *)string
options:(NSMatchingOptions)options range:(NSRange)
range usingBlock:(void (^)(NSTextCheckingResult
*result, NSMatchingFlags flags, BOOL *stop))block
- 27. APIs using Blocks (2/2)
// ALAssetsLibrary
- (void)enumerateGroupsWithTypes:(ALAssetsGroupType)
types usingBlock:(ALAssetsLib...ationResultsBlock)
enumerationBlock failureBlock:
(ALAssetsLib...ssFailureBlock)failureBlock
// Game Kit Patterns for accessors:
- (void)loadScoresWithCompletionHandler:(void (^)
callback / handler
(NSArray *scores, NSError *error))completionHandler
// AVFoundation, AppKit, many other, etc.
- 30. Threads
• Threads are not dead yet, they are just
under your feet. (see crashlogs)
• Some uses are well suited at threads
• Real time needs
• OpenGL
• Core Image
• ...
- 31. What’s Next?
• Check WWDC videos [210, 308, 322]
• Dispatch group (dispatch_group_*)
• Source (dispatch_source_*)
• Semaphores (dispatch_semaphore_*)
• ARC interactions
• File / Memory I/O
• Target queues