GCD is an extremely important feature introduced in iOS 4.0, OS X 10.6 and later versions.
Its used to distribute the computation across multiple cores dispatching any number of threads needed and it is optimized to work with devices with multi-core processors.
When using GCD you don’t need to handle any multiple threads yourself, GCD technology will take care of that for you, so you can get to focus on the task itself.
GCD works with Blocks, blocks of code that you need to execute in the background,
in low, default or high priority queue, in a serial or concurrent order.
Using GCD in your application will bring a great benefit of all the features mentioned above without the hassle of managing the threads your self and knowing that the GCD technology will be utilizing multi-core processors found in iOS devices today.
Before we dig into GCD itself we’ll need to be comfortable using blocks. Blocks are a C-level syntactic and runtime feature that let you pass a function as an argument, store it, or execute it on multiple threads. Blocks look very similar to functions, instead of the name of the function replace it with ^ and you’re good to go.
To learn more about blocks visit:
You can use a block for example as an argument in a method like :
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block
where the block parameter is expecting an object, idx and stop.
Here is one example of how to use a block for enumerating on an array:
NSArray *inputArray = [NSArray arrayWithObjects:@"1",@"2",@"3",@"4",[NSObject new],@"a",@"b", nil];
[inputArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
if (![obj isKindOfClass:[NSString class]]) {
NSLog(@"found the first different Object: %@",obj);
*stop = YES;
}
else {
NSLog(@"%@ at index:%d", obj, idx);
}
return;
}];
So the output will be:
1 at index:0
2 at index:1
3 at index:2
4 at index:3
found the first different Object:
Now that we know more about blocks, I’ll show you how you can add them to a serial queue.
Adding blocks to serial queues ensures that these blocks of code will be executed one after the other. If one block is taking too long, the rest of the blocks will have to wait until its done.
Serial blocks are useful in many cases where ensuring the order of execution is critical.
Lets say we have a block with this definition:
static void (^counterBlock)(int, char, NSUInteger, GCDMaster *) = ^(int count, char letter, NSUInteger blockNumber, GCDMaster *delegate){
… block does some work here
}
This block is named counterBlock and it takes arguments: int, char, NSUInteger and an object typed GCDMaster.
The arguments in this block are passed in from the method calling the block. As you saw earlier, you can specify any configuration of a block just like you would do with functions.
Now lets talk about Dispatch Queues. Apple’s definition of Dispatch Queues is:
Dispatch queues are a C-based mechanism for executing custom tasks. A dispatch queue executes tasks either serially or concurrently but always in a first-in, first-out order. (In other words, a dispatch queue always dequeues and starts tasks in the same order in which they were added to the queue.) A serial dispatch queue runs only one task at a time, waiting until that task is complete before dequeuing and starting a new one. By contrast, a concurrent dispatch queue starts as many tasks as it can without waiting for already started tasks to finish.
We want to dispatch a few of these blocks on our serial Queue.
Lets start by creating a serial queue:
dispatch_queue_t serialQueue = dispatch_queue_create("com.3Pillar.SerialQueue", NULL);
This will define a serial queue with a reverse domain name so we can identify our queues.
Next we will add 4 of these blocks with different arguments to run serially on the queue:
dispatch_async(serialQueue, ^{counterBlock(20000 ,'a' ,0 ,self);});
dispatch_async(serialQueue, ^{counterBlock(1000 ,'A' ,1 ,self);});
dispatch_async(serialQueue, ^{counterBlock(20000 ,'0' ,2 ,self);});
dispatch_async(serialQueue, ^{counterBlock(2000 ,'Z' ,3 ,self);});
What we did now is we added 4 blocks to our queue “serialQueue” with various different arguments. These blocks will be executed one after the other. When block 1 is finished 2 is started and so forth.
So if we have a log message when the block start and end it will look like this:
==block 1 started ==
==block 1 ended ==
==block 2 started ==
==block 2 ended ==
==block 3 started ==
…
But if we would want to execute the blocks concurrently and we don’t have the need to run them sequentially then we’ll need to create a concurrent queue.
Apple provides you with a number of concurrent queues that you can access from you’re application. These queues are global and they have 3 priorities DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_PRIORITY_DEFAULT and DISPATCH_QUEUE_PRIORITY_LOW priority.
To get a concurrent queue:
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
Then we add the blocks to the queue the same way as before:
dispatch_async(concurrentQueue, ^{counterBlock(20000 ,'a' ,0 ,self);});
dispatch_async(concurrentQueue, ^{counterBlock(1000 ,'A' ,1 ,self);});
dispatch_async(concurrentQueue, ^{counterBlock(20000 ,'0' ,2 ,self);});
dispatch_async(concurrentQueue, ^{counterBlock(2000 ,'Z' ,3 ,self);});
So GCD runs these blocks concurrently, there is no way to expect which one executes first.
So the output would be random like this:
==block 2 started ==
==block 3 started ==
==block 3 ended ==
==block 1 started ==
…
So once the Queue is running you can suspend it and resume it by calling the appropriate methods dispatch_suspend and dispatch_resume. Note that the calls to dispatch_suspend and dispatch_resume must be balanced.
Download project on GitHub
https://github.com/edwardIshaq/GCDDemo
No comments:
Post a Comment