Blocks allow passing code as a parameter or return value from functions. They capture the context in which they are defined. Grand Central Dispatch (GCD) simplifies concurrent programming using dispatch queues, which can execute blocks asynchronously or synchronously, and dispatch sources which attach blocks to system events. Dispatch groups and semaphores help coordinate work across dispatch queues.
3. What are Blocks?
Blocks are:
• an extension to the C language and its derivatives
Objective-C and C++, introduced by Apple
• available in iOS SDK since version 4.0
• already used by plenty of iOS frameworks’ APIs
Pragma Night
giovedì 7 febbraio 13
4. Benefits
• more functional style of coding
• less boilerplate code
• more readable code
• simplify event-driven scenarios (i.e. callbacks)
• simplify multi-threading
Pragma Night
giovedì 7 febbraio 13
5. Definition
• ‘block’ is the name Objective-C gives to the
concept of closure, that is:
• a pointer to a function
• a copy of some of the local variables of its
higher-order function
Pragma Night
giovedì 7 febbraio 13
6. Syntax
int multiplier = 7;
Declaration of the variable
Definition of the variable ‘myBlock’,
‘myBlock’.
using a block literal.
The ‘^’ tells that its type is a block.
int (^myBlock)(int) = ^(int num) { return num * multiplier; };
Return type Parameter list
Body of the block.
The parameter is
named ‘num’.
Pragma Night
giovedì 7 febbraio 13
7. Key features
• allow code to be passed around for later
execution
• access to the local variables of the function they
were declared in
• mantain a state among calls
Pragma Night
giovedì 7 febbraio 13
8. Code example #1
int multiplier = 7;
// Declaring a block and assigning it to the variable ‘myBlock’
int (^myBlock)(int) = ^(int num) { return num * multiplier; };
// Calling the block
printf(myBlock(3)); // prints '21'
Calling a block in no different
than calling a function
Pragma Night
giovedì 7 febbraio 13
9. Code example #2
// Inline use of a block literal (as an actual parameter of a function)
char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
char *left = *(char **)l;
char *right = *(char **)r;
return strncmp(left, right, 1);
});
qsort_b() will internally call the block many times, in order
to compare each pair of cells in the array
// myCharacters is now { "Charles Condomine", "George", "TomJohn" }
Pragma Night
giovedì 7 febbraio 13
10. Code example #3
// Declaring a function that takes a block as parameter
void myFunction(int (^aBlock)(void *, void *));
// Declaring a function that returns a block
void (^myFunction(int a, int b))(double, double);
Function name Function parameters The function’s return type is a block with signature
void (^)(double, double)
Pragma Night
giovedì 7 febbraio 13
11. Code example #4
// Defining a custom name for our block type to improve code style
typedef void (^MyBlockType)(double, double);
// Variable and function declarations now look much more readable
MyBlockType myBlock = ^(double a, double b) { printf("hey!"); };
MyBlockType myFunction(int a, int b, MyBlockType aBlock);
Pragma Night
giovedì 7 febbraio 13
12. Variables caveats
• non-local variables in blocks are constant and
read-only
Trying to modify ‘i’ from inside the block
results in a compilation error.
• in order to make them ‘live’ and writable the
__block type specifier must be added to their
declaration
Pragma Night
giovedì 7 febbraio 13
13. __block
int myFunction() {
__block int i = 0;
void (^myBlock)(void) = ^{
i++;
printf("i has the value %d", i); // prints 'i has the value 1'
};
}
• what __block does is:
• pass the variable by reference - rather than by
value - to the block
• create a strong (as opposed to weak) reference
to that variable
Pragma Night
giovedì 7 febbraio 13
14. State representation
• blocks can use variables marked with __block to
keep a state among calls
int myFunction() {
__block int i = 0;
void (^myBlock)(void) = ^{
i++;
printf("i has the value %d", i);
};
myBlock(); // prints 'i has the value 1'
myBlock(); // prints 'i has the value 2'
}
Pragma Night
giovedì 7 febbraio 13
15. Information hiding
The outer block defines a local variable that is used inside the
inner block to mantain a state: this way we are keeping the sum
variable hidden to the rest of the program
int array[] = {4, 5, 2, 6, 1};
qsort_b(array, 5, sizeof(int), ^(void) {
__block int sum = 0;
return ^(const void *a, const void *b) {
sum += (int)a;
return a - b;
}; The inner block is what gets passed to the qsort_b() function
}());
These parentheses tell us that the outer block is
executed, and not passed to qsort_b.
Pragma Night
giovedì 7 febbraio 13
16. __block implications
• variables marked with __block are shared
between their lexical scope and all blocks and
block copies declared or created within that scope
• multiple blocks can simultaneously use a shared
variable
• when a block is copied (i.e. it is moved from the
stack to the heap), they survive the destruction
of their stack frame
Pragma Night
giovedì 7 febbraio 13
17. Memory concerns
• a block is allowed to access self
• self is passed as a strong reference
• this could lead to a ‘retain cicle’
• trick: define a __weak reference to self
int myFunction() {
__weak MyObject weakSelf = self;
void (^myBlock)(void) = ^{
[weakSelf someMethod];
}
}
Pragma Night
giovedì 7 febbraio 13
18. Common scenarios in the SDK
Collection enumeration
NSArray *array = [NSArray arrayWithObjects:@"One", @"Two", @"Three", @“Four”, nil];
It substitutes the for loop.
Now the collections can enumerate themselves.
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"array[%d] = %@", idx, obj);
}]; Setting *stop to YES inside the
block will stop the enumeration
[array enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock:
^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"array[%d] = %@", idx, obj);
} Making an enumeration concurrent
]; is a matter of adding an option!
Pragma Night
giovedì 7 febbraio 13
19. Common scenarios in the SDK
View animations
- (void)animateView:(UIView*)view {
CGRect cacheFrame = [view frame];
[UIView animateWithDuration:1.5 animations:
^{
CGRect newFrame = [view frame];
newFrame.origin.y = newFrame.origin.y + 250.0;
[view setFrame:newFrame];
[view setAlpha:0.5];
}
completion:
^(BOOL finished) {
if (finished) {
sleep(1);
[view setFrame:cacheFrame];
[view setAlpha:1.0];
}
}];
}
Pragma Night
giovedì 7 febbraio 13
20. Common scenarios in the SDK
Notification observers
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[center addObserverForName:SomeNotificationName
object:nil
queue:mainQueue
usingBlock:^(NSNotification *note) {
NSLog(@"Notification received");
}];
Pragma Night
giovedì 7 febbraio 13
21. Common scenarios in the SDK
Operations queues
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSLog(@"This block is run in the operation");
}];
Wraps the block inside a NSOperation
A single NSBlockOperation can execute multiple blocks concurrently
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"This block is run in the operation");
}];
[operation addExecutionBlock:^{
NSLog(@"NSBlockOperations can execute multiple blocks ");
}];
[operation setCompletionBlock:^{
NSLog(@"This Code Runs Once The Operation Has Finished");
}];
Pragma Night
giovedì 7 febbraio 13
23. What is Grand Central Dispatch?
Grand Central Dispatch (GCD) is:
• a set of language features and libraries to improve
and simplify the act of writing concurrent code
• more efficient than threads
• implemented at all levels of APIs in iOS (BSD
subsystem, CoreFoundation, Cocoa)
Pragma Night
giovedì 7 febbraio 13
24. Concept
• GCD is based on the Thread Pool pattern
• a (small) number of threads is created
• (possibly lots of) tasks are added to queues in
order to be executed
• an algorithm handles the creation/destruction of
threads, and the scheduling of tasks
Pragma Night
giovedì 7 febbraio 13
25. Implementation
• dispatch queues
• dispatch sources
• dispatch groups
• dispatch semaphores
Pragma Night
giovedì 7 febbraio 13
26. Dispatch queues
• execute tasks always in a first-in, first-out order
• 2 types:
• serial queues (aka private dispatch queues)
• one and only one task running at a time
• the main dispatch queue is a peculiar one
• concurrent queues (aka global dispatch queues)
• tasks started in order but run concurrently
• four such queues, differing only by priority level,
are made available by the os
Pragma Night
giovedì 7 febbraio 13
27. Serial queues
• one and only one task running at a time
• the main dispatch queue is serial
• tied to the main thread and application’s run loop
• interleaves queued tasks with other event
sources
• often used as the key synchronization point for
the application
Pragma Night
giovedì 7 febbraio 13
28. Creating serial queues
// Creating a serial dispatch queue
dispatch_queue_t queue;
queue = dispatch_queue_create("com.example.MyQueue", NULL);
// Getting the dispatch queue on which the currently executing block is running
dispatch_queue_t current_queue;
current_queue = dispatch_get_current_queue();
// Getting the main dispatch queue
dispatch_queue_t main_queue;
main_queue = dispatch_get_main_queue();
Pragma Night
giovedì 7 febbraio 13
29. Concurrent queues
• tasks are started in order but run concurrently
• the system provides four concurrent queues
• they are global to the application
• they differ only by priority level
Pragma Night
giovedì 7 febbraio 13
30. Creating concurrent queues
// Getting one of the four global dispatch queues
dispatch_queue_t global_queue;
global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// The four priority levels, ranked high to low, are
DISPATCH_QUEUE_PRIORITY_HIGH
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND
// Creating a concurrent dispatch queue
dispatch_queue_t queue;
queue = dispatch_queue_create("com.example.MyQueue", DISPATCH_QUEUE_CONCURRENT);
Pragma Night
giovedì 7 febbraio 13
31. Using queues
• dispatch_async(queue, block)
• submits a block for asynchronous execution on
a dispatch queue and returns immediately
• dispatch_sync(queue, block)
• submits a block object for execution on a
dispatch queue and waits until that block
completes
Pragma Night
giovedì 7 febbraio 13
32. Using queues
• dispatch_after(when, queue, block)
• enqueue a block for execution at the specified time
• dispatch_apply(iterations, queue, block)
• submits a block to a dispatch queue for multiple
invocations
• dispatch_once(queue, block)
• executes a block object once and only once for the
lifetime of an application
Pragma Night
giovedì 7 febbraio 13
33. Dispatch sources
• allow the client to register blocks or functions to
execute asynchronously upon system events
• unlike manually-put tasks, they remain attached to
their queue, and submit their associated task to it
whenever the corresponding event occurs
• to prevent backlogging, they can coalesce events
• types of sources: signal, timer, descriptor, process,
Mach port, custom
Pragma Night
giovedì 7 febbraio 13
34. Dispatch source example
dispatch_source_t CreateDispatchTimer(uint64_t interval,
uint64_t leeway,
dispatch_queue_t queue,
dispatch_block_t block) {
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
0, 0, queue);
if (timer) {
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);
dispatch_source_set_event_handler(timer, block);
dispatch_resume(timer);
}
return timer;
}
void MyCreateTimer() {
dispatch_source_t aTimer = CreateDispatchTimer(30ull * NSEC_PER_SEC,
1ull * NSEC_PER_SEC,
dispatch_get_main_queue(),
^{ MyPeriodicTask(); });
// Store it somewhere for later use.
if (aTimer) {
MyStoreTimer(aTimer);
}
}
Pragma Night
giovedì 7 febbraio 13
35. Dispatch groups
• are objects that allow several tasks to be grouped
for later joining.
• a task can be added to a queue as a member of a
group, and then the client can use the group object
to wait until all of the tasks in that group have
completed
Pragma Night
giovedì 7 febbraio 13
36. Dispatch group example
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
// Add a task to the group
dispatch_group_async(group, queue, ^{
// Some asynchronous work
});
//
// Do some other work while the tasks execute...
//
// When you cannot make any more forward progress,
// wait on the group to block the current thread.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// Release the group when it is no longer needed.
dispatch_release(group);
Pragma Night
giovedì 7 febbraio 13
37. Dispatch semaphores
• useful for regulating the use of finite resources
// Example: limiting the number of file descriptors open concurrently
// Create the semaphore, specifying the initial pool size
dispatch_semaphore_t fd_sema = dispatch_semaphore_create(getdtablesize() / 2);
// Wait for a free file descriptor
dispatch_semaphore_wait(fd_sema, DISPATCH_TIME_FOREVER);
fd = open("/etc/services", O_RDONLY);
// Release the file descriptor when done
close(fd);
dispatch_semaphore_signal(fd_sema);
Pragma Night
giovedì 7 febbraio 13
38. References
• WWDC 2011 - session 308:
Blocks and Grand Central Dispatch in Practice
• Apple documentation
• Blocks Programming Topics
• Concurrency Programming Guide
• Grand Central Dispatch (GCD) Reference
• Book: Pro Multithreading and Memory Management
for iOS and OS X: with ARC, Grand Central
Dispatch, and Blocks
Pragma Night
giovedì 7 febbraio 13
39. Thank You!
matteo.battaglio@pragmamark.org
Pragma Night
giovedì 7 febbraio 13