About implementing asynchronous non-blocking in block + gcd (Grand Central Dispatch) in IOS

Source: Internet
Author: User
From: http://bbs.et8.net/bbs/showthread.php? T = 1019931 this document uses an example to illustrate how to use block + GCD in IOS Program To implement non-blocking execution of time-consuming tasks. First, I would like to explain that the concepts of "Asynchronous", "Background thread", and "non-blocking" have some residential areas. Some system APIs, especially network and file I/O, implement "non-blocking" by means of system underlying interruptions, while general user tasks such as time-consuming computing are completed through background threads. But when it comes to the app layer, developers are not concerned about whether hardware interruptions or threads are used for the specific implementation. Therefore, in the context of this article, we have not specifically distinguished these concepts, it may even be mixed. The "non-blocking" in this article can be simply understood as, developers only need to know that "when my program executes time-consuming tasks, the UI can still respond to user operations ".

DemonstrationCodeIn the attachment. It can be compiled with xcode 4 and run on iOS 4 and later versions.

Anyone who has written a program knows that to make the program respond to user input in a timely manner and avoid program freezing in a certain operation, it is necessary to put time-consuming operations in the background, then the process goes down through asynchronous notifications or callbacks. Otherwise, the time-consuming operation will block the main thread, causing the program to not return to the main event loop for a long time.

This is especially important on the mobile platform. Generally, the system on the mobile platform has a special inspection mechanism to check whether the program has been blocked for a long time and does not return to check the master message queue. In this case, the program is usually killed as "no response. Generally, IOS is up to 10 seconds. The program is killed if it does not return to the main message loop within 10 seconds. It takes about 5 seconds to switch between the frontend and backend. (In general PC programming, there is a higher degree of tolerance for this situation. The program itself will be frozen and the UI screen will be stopped (so blank or broken windows will often be seen ), sometimes the system prompts a "stop response" Warning. But in general, the system will not take the initiative to kill these programs)

However, for many developers, especially new users, this non-blocking method is a violation of human intuitive thinking. For example, when a user clicks a button, I want to calculate the 1 million-bit pI value in the program. Starting from the most intuitive thinking, we generally first think of a sequential programming method:

Code:
 
// Example 1: blocking method // the user clicks a button to trigger the computing operation-(void) didtapcalcbutton {// The prompt "Please wait" appears [self showwaitingview]; // calculate the PI value to 1 million bits. It is returned only after the operation is completed. Nsstring * result = [self calcpi: 1000000]; // If you disable the "Please wait" prompt, [self hidewaitingview]; // display the result (of course, only the first n digits may be displayed here, otherwise it becomes time-consuming.) [self displayresult: result];}

This has many problems. First, the program mentioned above does not respond to user input, or even is determined by the system to kill the problem of losing the response. Second, the prompt "Please wait" does not appear at all. In iOS, any operations on the UI are not executed immediately, but are marked. After the current event loop is completed, before the next event loop starts, the system determines which part of the screen needs to be updated and re-painted Based on the mark. As shown in the preceding code, showloadingview only makes a tag, but the current runloop will end after the function returns. We call hideloadingview before the end, so it is not displayed at all.

To solve these problems, you need to put the PI value calculation operation in the background for asynchronous execution. There are many specific methods. The traditional method is nothing more than self-opening the thread, or using the advanced thread encapsulation nsoperation provided by IOS to do so. (Keep away: there is another way on low-end mobile platforms that do not have thread support, that is, to do a very small amount of computing each time to avoid blocking, such as computing 100 bits, then, schedule the remaining work to the end of the event queue. Repeat the process and the final result is completed 10 thousand times. This method is very inefficient, and developers need to save several statuses themselves, which is very troublesome)

Either way, the program structure of the traditional Asynchronous Method to implement this example is probably like this:

Code:
// Example 2: The traditional background tasks are asynchronous. // the user clicks a button to trigger the computing operation.-(void) didtapcalcbutton {// The prompt "Please wait" appears. [self showwaitingview]; // calculate the PI value to 1 million bits. Here we only create a background task and start it, and then return immediately, without waiting for the task to complete [self startcalcpi: 1000000]; // then the program to do nothing .} // No matter which Asynchronous Method, there must be a way to notify the main thread that the task has been completed. For iOS, there are several methods to use, such as: // delegate, KVO, nsnotification, and javasmselecw.mainthread. // Assume that the following callback function is called on the main thread:-(void) calculationdidfinishwithresult :( nsstring *) Result {// when "Please wait" is disabled, the system prompts [self hidewaitingview]; // display the result on the screen [self displayresult: result];} // The following example uses nsoperation + KVO for background computation. -(Void) startcalcpi :( nsinteger) digits {// mypicalcoperation is a subclass of nsoperation. The main method directly performs PI operations, which is equivalent to the calcpi: In the blocking example :. Nsoperation * calcopeation = [[[mypicalcopeartion alloc] init] autorelease]; // suppose we choose to use KVO to observe the end Of the background task [calcoperation addobserver: Self keypath: @ "isfinished"…]; // Submit the task and start execution. [Self. operationqueue addoperation: calcoperation];} // observe the completion of background computing tasks. This is a standard KVO function. Simply put, when the isfinished attribute of calcoperation changes from false to true, it will be called-(void) observevalueforkey: keypath ofobject: object... {If ([@ "isfinished" isinclutostring: keypath] & [object iskindofclass: [mypicalcoperation class]) {// we have observed the desired state change, that is, the operation is over. Here we call the callback processing result. Ensure that the callback is performed on the main thread mypicalcopeartion * op = (mypicalcoperation *) object; [self defined mselec1_mainthread: @ selector (calculationdidfinishwithresult :) withobject: op. resultwaituntildone: false];} else {[Super observevalueforkey:...];}

This is almost the case. Of course, the amount of specific code is related to the specific asynchronous implementation you choose, but there is always an additional code to do things in the background to determine the end of the operation and callback. There is no fundamental difference in this regard.

This is a natural thing for skilled developers, especially a qualified mobile platform developer, who will think it is necessary to write a program. But now the problem is that with the emergence of Android/IOS, more and more non-professionals are writing programs. They have a great idea and can make very useful things, but they have not received orthodox programming training after all, so many people will think that the Asynchronous Method is difficult to understand and the code is complex (look at the code comparison in the above two examples. In the second example, I also omitted the implementation of mycalcpioperation, otherwise it will be longer ). Therefore, many naturally choose to write programs in synchronous blocking mode. The result is that there are a large number of unstable programs on the appstore, which somehow crash. Or the iPhone 4 can work normally, but it crashes on a slower 3gs, because the slow computing speed leads to a long blocking time.

However, the traditional Asynchronous Method takes some time to master and is prone to some common errors. For example, the registration and anti-Registration of KVO do not match. It is not clear whether the function is executed on the main thread or backend thread, resulting in invalid UI operations. However, the delegate mode often causes memory problems, for example, retain delegate causes loop reference; or assign delegate is not properly managed, and a wild pointer is displayed. This type of problem can discourage common developers.

Ios4 introduces block programming and gcd (Grand Central Dispatch) task queue management. Here we will not go to the layout to introduce the boring syntax. If you have any requirements, please read the documents by yourself. First, we try to use block + GCD to rewrite the program segment that calculates the 1 million-bit Pi:

Code:
// Example 3: block + GCD asynchronous // the user clicks the button to trigger the calculation operation-(void) didtapcalcbutton {// The prompt "Please wait" appears [self showwaitingview]; // The following two lines schedule tasks to one background thread for execution. Dispatch_get_global_queue obtains a backend task queue allocated by the system. Dispatch_queue_t queue = dispatch_get_global_queue (dispatch_queue_priority_default, 0); dispatch_async (queue, ^ {// calculates the PI value to 1 million bits. Same as calcpi in Example 1: The only difference is that it is executed on the background thread. Nsstring * result = [self calcpi: 1000000]; // after calculation, you need to switch back to the main thread because of UI operations. General principle: // 1. UI operations must be completed on the main thread. 2. Do not run time-consuming synchronization network, synchronous Io, and operations on the main thread to avoid blocking. // dispatch_get_main_queue () will return the task queue associated with the main thread. Dispatch_async (dispatch_get_main_queue (), ^ {// close the "Please wait" prompt [self hidewaitingview]; // display the result [self displayresult: result] ;});});}

Then ...... Then there will be no more. That's it.

The following are examples 1 and 3. Basically the same code. 3 only adds two lines of dispatch commands to switch tasks between the front-end and backend. But 3 will not cause the main thread to block at all, even if it takes an hour to calculate the PI value, there will be no problem. The "Please wait" prompt can also be correctly displayed and disappear. Block + GCD can be said to keep the sequence programming intuitive and concise, and implement asynchronous features technically to improve program response. It can be said that it is a perfect method, and the code is also very easy to understand.

Here are some notes for example 3:

1. didtapcalcbutton does not return the result until the calculation is complete. When a computing task is thrown into the backend Queue (or even not necessarily started), it is immediately returned. Subsequent operations should be remembered and completed by the system itself

2. A major feature of block is automatic variable lifecycle management. In traditional asynchronous mode, the computing state or result must be reserved as a member variable of the class. In Example 3, nsstring * result is directly applied as a local variable and can be directly used in another block. This is a more revolutionary approach, because from the perspective of the Traditional variable life cycle, the result variable is only valid in the first block. In the last section where the displayresult is located, the scope should be generated and is no longer valid. But for block, the compiler does some special things. It will automatically analyze the cross-block reference of variables and perform cross-block transfer (the _ block method is required), pass the value, or retain (call the object or its attributes and methods ). Therefore, for developers, the variable life cycle between blocks is flexible, basically "available after the previous definition ".

If you are familiar with Java inner class, the second point is very similar. Non-static inner class allows access to local variables of the outer layer, but the outer layer must be applied in final mode. However, Java has no address transfer mode (equivalent to _ block), so inner class cannot modify the value of external local variables (if I remember correctly ). Inner class references member variables and methods of the external layer class. The Compiler also creates a series of anonymous methods such as outter. Access $100. From this point of view, block draws on a lot of Java inner class concepts. GCD only manages a bunch of foreground backend task queues and allows programs to switch tasks between queues. GCD selects block as the syntax defined by the task, because block is suitable for automatic cross-block lifecycle management.

Note that this method is not omnipotent:

1. A good program provides users with a half-canceled option for any time-consuming operations. To do this, you still need to add some code

2. Block is like an object, and it also has its own life cycle problem. It may also cause leakage of similar wild pointers and memory. If you create a block-based Asynchronous library for others' use, it is very easy to generate loop reference errors (the other app class releads your asynchronous library, your asynchronous library retain the callback block provided by the app, and the block generally references the app class itself through self). Be careful.

3. If you exit the page (for example, roll back to the previous page) before the operation is complete, the Operation will continue, and the destruction of View Controller will be postponed until the operation ends. If you do not want this effect, you need to implement the cancellation mechanism in step 1, and avoid referencing self in the block (otherwise it will be automatically retaken ). For more information, see the document.

For more information, see. Errors and omissions are inevitable. Welcome to the discussion.

The appendix is the Demo code. Three classes nbexample1, 2, and 3viewcontroller demonstrate three methods respectively. We can see that the synchronization method of Example 1 has a poor experience, and the program may be suspended by the system. 2 & 3 achieves non-blocking, while the UI of the task is in progress can also respond (the list can be rolled), but the code of 3 is much simpler.

Refer:

Http://developer.apple.com/library/m...8091-CH102-SW1

Http://developer.apple.com/library/ I ...roduction.html

Uploaded attachment
Nonblockingtasks.zip (80.8 kb, 10 views)
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.