Original link: http://www.raywenderlich.com/4295/multithreading-and-grand-central-dispatch-on-ios-for-beginners-tutorial
Next: Tutorial on multithreading and gcd (i)
One simple way to do this is to refresh the other part of the code from part of your code, which is Apple's built-in nsnotification messaging system. It's really simple. You can pass [Nsnotificationcenter Defaultcenter] Get the Nsnotificationcenter (Message Center) Singleton and:
1. If you have a place where you want to refresh the UI or code, you should call the Postnotificationname method. You just need to provide a unique string (for example: com.razeware.imagegrabber.imageupdated) and an object (such as the picture class imageinfo you just finished downloading)
2. If you want to find out when this update occurs, you can call the AddObserver:selector:name:object method. In our example, Imagelistviewcontroller will notice when the update occurs, So it can load the TableView cell appropriately. The best place to put this function is in the viewdidload:
3. Do not forget to call RemoveObserver:name:object when the view is about to be destroyed. Otherwise, the message system may invoke other methods in the view that you destroyed (or worse, an object that is not allocated memory), which has serious consequences.
So let's try to solve it. Open IMAGEINFO.M and make the following changes
// Add inside getImage, right after image = [[UIImage alloc] initWithData:data];[[NSNotificationCenter defaultCenter] postNotificationName:@"com.razeware.imagegrabber.imageupdated" object:self];
Once the image is downloaded, we send a notification and pass the newly updated object over.
Next go to IMAGELISTVIEWCONTROLLER.M and make the following changes:
//At end of Viewdidload[[NsnotificationcenterDefaultcenter] Addobserver: SelfSelector@selector(imageupdated:) name:@"com.razeware.imagegrabber.imageupdated"ObjectNil];//At end of Viewdidunload[[NsnotificationcenterDefaultcenter] Removeobserver: Selfname:@"com.razeware.imagegrabber.imageupdated"ObjectNil];//Add New method- (void) Imageupdated: (nsnotification*) Notif {imageinfo * info = [Notif object];introw = [Imageinfos indexofobject:info];Nsindexpath* Indexpath = [NsindexpathIndexpathforrow:row insection:0];NSLog(@"Image for row%d updated!", row); [ Self. TableViewreloadrowsatindexpaths:[NsarrayArraywithobject:indexpath] withrowanimation:uitableviewrowanimationnone];}
In the Viewdidunload method, the registrar for Notifications is simply: "Call Imageupdate when the message arrives." Similarly, we can also cancel the registrar appropriately in the Viewdidunload method.
Call Imageupdate is essentially an array of imageinfo that are passed in the object. Once it is found, we can get what it is and tell TableView to update the line.
After the compilation is run, you will see the images jump out after they have been downloaded.
GCD and dispatch the queue, my goodness!
There is still a problem in our application. If you click on "grab!" And when the detail view is loaded, it continues to slide up and down, and when the zip file is downloaded you will see all the UI Blues storing and decompression zip files and being frozen.
This is because the completion block in ASIHTTPRequest is called in the main thread, and we call this code to resolve these issues in the main thread:
[request setCompletionBlock:^{
NSLog(@"Zip file downloaded.");
NSData *data = [request responseData];
[self processZip:data sourceURL:sourceURL]; // Ack - heavy work on main thread!
}];
How can we get this high-load job running in the background?
The ios3.2 introduced a very simple (and very effective) way to solve this problem through the GCD system. Fundamentally, whenever you want to run something in the background, you can call Dispatch_async and write some code that you want to run.
GCD will solve all the details for you-it will create a new thread for you when you need it, or reuse a previous available thread.
When you call Dispatch_async, you enter a dispatch queue (Dispatch_queue). You can think of this as a FIFO list to store all the code blocks you write.
You can create your dispatch queue (via Dispatch_create). Or get a special main thread queue (via Dispatch_get_main_queue). We can create a background queue called "Backgroundqueue", Use it to run process tasks in the background, like parsing XML or storing/extracting zip files.
Thread queue, lock, and cat food
When a dispatch queue is created, it is contiguous by default-which means that only one piece of code can run in the queue at the same time. This is really handy because you can protect your data in this way.
If you are not very familiar with multi-threaded lock, think about our previous cat example. What happens if two cats both think of eating cat food in cat food boxes? That's the big question!
But if we make all the cats in a row. And then we say, "meow, if you want to get close to this plate, you have to stand on this line!" As long as it is so much easier!
That's the basic idea of using a dispatch queue to protect your data. When you write code to make a special data structure available only to code that runs in the dispatch queue. Then, as the dispatch queue runs continuously, you will ensure that only one block of code accesses the data structure at the same time.
In this application we need to protect two data structures:
1. Linkurls array in Imagelistviewcontroller. To protect it, we write code to make it run only in the main thread.
2. The pendingzips variable in Imagemanager. To protect it, we write code so that it runs only in the background queue.
About GCD we had enough to talk about-let's try it!
GCD Practice
Let's start with opening ImageGrabber.h and make the following changes:
// Add to top of file#import <dispatch/dispatch.h>// Add new instance variabledispatch_queue_t backgroundQueue;
In order to use GCD, you first need to import. We also need to pre-compile the dispatch queue to run our background process tasks.
The next step is to open imagegrabber.m and make the following changes:
//1) Add to bottom of initwithhtml:delegateBackgroundqueue = Dispatch_queue_create ("Com.razeware.imagegrabber.bgqueue",NULL);//2) Add to top of DeallocDispatch_release (Backgroundqueue);//3) Modify process to being the following- (void) Process {Dispatch_async(Backgroundqueue, ^ (void) { [ SelfProcesshtml]; }); }//4) Modify call to Processzip inside Retrievezip to be the followingDispatch_async(Backgroundqueue, ^ (void) { [ SelfProcesszip:data Sourceurl:sourceurl];});//5) Modify call to delegate at the end of processhtml **and** Processzip to be the followingDispatch_async(Dispatch_get_main_queue (), ^ (void{[Delegate Imageinfosavailable:imageinfos done: (pendingzips==0)];});
These are simple but important calls, let's talk about them all in one go.
1. This creates a dispatch queue. When you create a dispatch queue, you need to give him a unique name (character form)
2. When you create a dispatch queue, do not forget to release it! For this queue, we need to release it when the Imagemanager is destroyed.
3. The old process runs processhtml immediately, so it blocks UI refreshes when it runs parsing HTML in the main thread. Now we're going to run it in the background queue we created, simply call dispatch_async!
4. Similarly, after we download the zip we will get a asihttirequest callback in the main thread: "Hey, I'm done." As an alternative to blocking UI refreshes as we previously stored and unzipped the zip file, We will now run it in the background queue. This is also important for guaranteeing pendingzips variables.
5. We want to make sure that we invoke the proxy method in the context of the main thread. First, based on our previous strategy, To ensure that the Linkurls array in the Viewcontroller is accessible only through the main thread. Second, this method interacts with Uikit objects, and the Uikit object can only be used by the main thread.
that's the point! Compile and run your code. Imagegrabber should have done a better job.
Http://cdn1.raywenderlich.com/wp-content/uploads/2011/07/ResponsiveApp.jpg
But wait a minute!
If you are only temporarily developing iOS, you may hear these ideas called nsoperations and the operations queue. You may wonder when to use them and when to use GCD.
Nsoperations is just a simple API based on GCD. So when you're using nsoperation, you're still using GCD.
Nsoperations will give you some of the features you like. You can create some actions based on other actions, rearrange your queues and similar features after you submit the queue.
In fact, Imagegrabber has used the nsoperations and operation queue! ASIHTTPRequest use them at the bottom, you can configure the operation queue as you want.
So which one should you use? Depending on your application, he is simple about this application, so we only need to use GCD, no other nsoperation features. But if you need these features in your application, use them quickly!
About multithreading and GCD Novice Tutorials (ii)