Use GCD for IOS (multi-core programming)

Source: Internet
Author: User
Tags call back

What is GCD?
Grand Central Dispatch (GCD) is a multi-core programming solution developed by Apple. This method was first introduced in Mac OS X 10.6 snow leopard and subsequently introduced to iOS4.0. GCD is an efficient and powerful technology that replaces technologies such as NSThread, NSOperationQueue, and NSInvocationOperation. It looks like Closure in other languages, but Apple calls it blocks.

Application Example
Let's look at a programming scenario. We want to make a Web page download function on the iphone. This function is very simple, that is, place a button on the iphone. When you click this button, a circle is displayed, the download is in progress. After the download is complete, the content is loaded into a text control on the interface.

Before using GCD
Although the function is simple, we must put the download process in the background thread; otherwise, the UI thread will be blocked. Therefore, if you do not need GCD, We need to write the following three methods:

The someClick method is the code after clicking the button. We can see that we have created a background thread using NSInvocationOperation and put it in NSOperationQueue. The background thread executes the download method.
The download method processes the logic of the downloaded webpage. After the download is complete, run the download_completed method with performselecdomainmainthread.
Download_completed to clear up and display the downloaded content to the text control.
The code for these three methods is as follows. As you can see, although the three steps start to download-> download in progress-> download completion are three steps of the entire function. However, they are split into three parts. Because there are three methods between them, data parameters need to be passed. For complex applications, the data parameters may not be as simple as the NSString in this example. In addition, the download may be placed in the Model class, the interface control is implemented at the View Controller layer, which further disperses the code originally separated. The readability of the Code is greatly reduced.

 

[Cpp]
Static NSOperationQueue * queue;
-(IBAction) someClick :( id) sender {
Self. indicator. hidden = NO;
[Self. indicator startAnimating];
Queue = [[NSOperationQueue alloc] init];
NSInvocationOperation * op = [[NSInvocationOperation alloc] initWithTarget: self selector: @ selector (download) object: nil] autorelease];
[Queue addOperation: op];
}
 
-(Void) download {
NSURL * url = [NSURL URLWithString: @ "http://www.youdao.com"];
NSError * error;
NSString * data = [NSString stringWithContentsOfURL: url encoding: NSUTF8StringEncoding error: & error];
If (data! = Nil ){
[Self initialize mselecw.mainthread: @ selector (download_completed :) withObject: data waitUntilDone: NO];
} Else {
NSLog (@ "error when download: % @", error );
[Queue release];
}
}
 
-(Void) download_completed :( NSString *) data {
NSLog (@ "call back ");
[Self. indicator stopAnimating];
Self. indicator. hidden = YES;
Self. content. text = data;
[Queue release];
}

Static NSOperationQueue * queue;
-(IBAction) someClick :( id) sender {
Self. indicator. hidden = NO;
[Self. indicator startAnimating];
Queue = [[NSOperationQueue alloc] init];
NSInvocationOperation * op = [[NSInvocationOperation alloc] initWithTarget: self selector: @ selector (download) object: nil] autorelease];
[Queue addOperation: op];
}

-(Void) download {
NSURL * url = [NSURL URLWithString: @ "http://www.youdao.com"];
NSError * error;
NSString * data = [NSString stringWithContentsOfURL: url encoding: NSUTF8StringEncoding error: & error];
If (data! = Nil ){
[Self initialize mselecw.mainthread: @ selector (download_completed :) withObject: data waitUntilDone: NO];
} Else {
NSLog (@ "error when download: % @", error );
[Queue release];
}
}

-(Void) download_completed :( NSString *) data {
NSLog (@ "call back ");
[Self. indicator stopAnimating];
Self. indicator. hidden = YES;
Self. content. text = data;
[Queue release];
}


After using GCD
If you use GCD, the above three methods can be put together, as shown below:


[Cpp]
// Original code block 1
Self. indicator. hidden = NO;
[Self. indicator startAnimating];
Dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
// Original code block 2
NSURL * url = [NSURL URLWithString: @ "http://www.youdao.com"];
NSError * error;
NSString * data = [NSString stringWithContentsOfURL: url encoding: NSUTF8StringEncoding error: & error];
If (data! = Nil ){
// Original code block 3
Dispatch_async (dispatch_get_main_queue (), ^ {
[Self. indicator stopAnimating];
Self. indicator. hidden = YES;
Self. content. text = data;
});
} Else {
NSLog (@ "error when download: % @", error );
}
});

// Original code block 1
Self. indicator. hidden = NO;
[Self. indicator startAnimating];
Dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
// Original code block 2
NSURL * url = [NSURL URLWithString: @ "http://www.youdao.com"];
NSError * error;
NSString * data = [NSString stringWithContentsOfURL: url encoding: NSUTF8StringEncoding error: & error];
If (data! = Nil ){
// Original code block 3
Dispatch_async (dispatch_get_main_queue (), ^ {
[Self. indicator stopAnimating];
Self. indicator. hidden = YES;
Self. content. text = data;
});
} Else {
NSLog (@ "error when download: % @", error );
}
});

 

First, we can see that the Code has become shorter. Because the definition of the original three methods is missing, and the encapsulation of variables to be passed between them is also missing.

In addition, the Code becomes clearer. Although it is asynchronous code, they are reasonably integrated by GCD, and the logic is very clear. If the MVC mode is applied, we can also pass the callback function of the View Controller layer to the Modal layer in the form of GCD, which is different from the previous method of @ selector, the logic of the Code is clearer.

GCD Definition
The definition of a simple GCD is a bit like a function pointer. The difference is that ^ replaces the * Number of the function pointer, as shown below:


[Cpp]
// Declare the variable
(Void) (^ loggerBlock) (void );
// Define
 
LoggerBlock = ^ {
NSLog (@ "Hello world ");
};
// Call
LoggerBlock ();

// Declare the variable
(Void) (^ loggerBlock) (void );
// Define

LoggerBlock = ^ {
NSLog (@ "Hello world ");
};
// Call
LoggerBlock ();

 

But most of the time, we usually define it in inline mode, and write its program block in the called function, for example:


[Cpp]
Dispatch_async (dispatch_get_global_queue (0, 0), ^ {
// Something
});

Dispatch_async (dispatch_get_global_queue (0, 0), ^ {
// Something
});

 

As you can see above, block has the following features:

The block can be defined inline in the code.
The program block can access available variables within the scope of creation.
The dispatch method provided by the system
To facilitate GCD usage, Apple provides some methods to facilitate block execution in the main thread or background thread, or delay execution. An example is as follows:


[Cpp]
// Background execution:
Dispatch_async (dispatch_get_global_queue (0, 0), ^ {
// Something
});
// Main thread execution:
Dispatch_async (dispatch_get_main_queue (), ^ {
// Something
});
// One-time execution:
Static dispatch_once_t onceToken;
Dispatch_once (& onceToken, ^ {
// Code to be executed once
});
// 2 seconds delayed execution:
Double delayInSeconds = 2.0;
Dispatch_time_t popTime = dispatch_time (DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC );
Dispatch_after (popTime, dispatch_get_main_queue (), ^ (void ){
// Code to be executed on the main queue after delay
});

// Background execution:
Dispatch_async (dispatch_get_global_queue (0, 0), ^ {
// Something
});
// Main thread execution:
Dispatch_async (dispatch_get_main_queue (), ^ {
// Something
});
// One-time execution:
Static dispatch_once_t onceToken;
Dispatch_once (& onceToken, ^ {
// Code to be executed once
});
// 2 seconds delayed execution:
Double delayInSeconds = 2.0;
Dispatch_time_t popTime = dispatch_time (DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC );
Dispatch_after (popTime, dispatch_get_main_queue (), ^ (void ){
// Code to be executed on the main queue after delay
});

 

You can also define dispatch_queue_t. to customize a queue, you can use the dispatch_queue_create method, for example:


[Cpp]
Dispatch_queue_t urls_queue = dispatch_queue_create ("blog.devtang.com", NULL );
Dispatch_async (urls_queue, ^ {
// Your code
});
Dispatch_release (urls_queue );

Dispatch_queue_t urls_queue = dispatch_queue_create ("blog.devtang.com", NULL );
Dispatch_async (urls_queue, ^ {
// Your code
});
Dispatch_release (urls_queue );

 

In addition, GCD also has some advanced usage, for example, to let two threads in the background execute in parallel, and then wait until the two threads finish, and then summarize the execution results. This can be implemented using dispatch_group, dispatch_group_async, and dispatch_group_notify. The example is as follows:


[Cpp]
Dispatch_group_t group = dispatch_group_create ();
Dispatch_group_async (group, dispatch_get_global_queue (0, 0), ^ {
// Thread 1 for Parallel Execution
});
Dispatch_group_async (group, dispatch_get_global_queue (0, 0), ^ {
// Thread 2 for Parallel Execution
});
Dispatch_group_notify (group, dispatch_get_global_queue (0, 0), ^ {
// Summary Result
});

Dispatch_group_t group = dispatch_group_create ();
Dispatch_group_async (group, dispatch_get_global_queue (0, 0), ^ {
// Thread 1 for Parallel Execution
});
Dispatch_group_async (group, dispatch_get_global_queue (0, 0), ^ {
// Thread 2 for Parallel Execution
});
Dispatch_group_notify (group, dispatch_get_global_queue (0, 0), ^ {
// Summary Result
});

 

Modify variables outside the block
By default, the external variables accessed in the program block are copied, that is, the write operation does not take effect for the original variables. However, you can add _ block to make the write operation take effect. The sample code is as follows:


[Cpp]
_ Block int a = 0;
Void (^ foo) (void) = ^ {
A = 1;
}
Foo ();
// Here, the value of a is changed to 1

_ Block int a = 0;
Void (^ foo) (void) = ^ {
A = 1;
}
Foo ();
// Here, the value of a is changed to 1

 

Background running
Another use of GCD is to allow programs to run for a long time in the background. When GCD is not used, when the app is exited by pressing the home key, the app only needs 5 seconds to save or clear resources. However, after using GCD, the app can run for up to 10 minutes in the background for a long time. This time can be used to clear the local cache and send statistics.

The sample code for long-running programs in the background is as follows:


[Cpp]
// AppDelegate. h file
@ Property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundUpdateTask;
 
// AppDelegate. m file
-(Void) applicationDidEnterBackground :( UIApplication *) application
{
[Self beingBackgroundUpdateTask];
// Add the code you need to run for a long time
[Self endBackgroundUpdateTask];
}
 
-(Void) beingBackgroundUpdateTask
{
Self. backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^ {
[Self endBackgroundUpdateTask];
}];
}
 
-(Void) endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask: self. backgroundUpdateTask];
Self. backgroundUpdateTask = UIBackgroundTaskInvalid;
}

// AppDelegate. h file
@ Property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundUpdateTask;

// AppDelegate. m file
-(Void) applicationDidEnterBackground :( UIApplication *) application
{
[Self beingBackgroundUpdateTask];
// Add the code you need to run for a long time
[Self endBackgroundUpdateTask];
}

-(Void) beingBackgroundUpdateTask
{
Self. backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^ {
[Self endBackgroundUpdateTask];
}];
}

-(Void) endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask: self. backgroundUpdateTask];
Self. backgroundUpdateTask = UIBackgroundTaskInvalid;
}

 

Summary
In general, GCD can greatly facilitate developers in multi-threaded programming. If your app does not support systems lower than iOS4.0, you should try to use GCD to process the interaction between background threads and UI threads.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.