The use of NSThread for iOS multi-threaded programming ( recommended, like the original author )

The use of NSThread for iOS multi-threaded programming ( recommended, like the original author )

Article source: blog.csdn.net/totogo2010/...

1 Introduction:

1.1 iOS has three multi-threaded programming techniques, namely:

1. NSThread 

2. Cocoa NSOperation  ( the use of NSOperation and NSOperationQueue in iOS multithreaded programming )

3. The   full name of GCD : Grand Central Dispatch (the  introduction and use of Grand Central Dispatch (GCD) for iOS multi-threaded programming )

These three programming methods are from top to bottom, and the level of abstraction is from low to high. The higher the abstraction, the easier it is to use. It is also the most recommended by Apple.

In this article, we mainly introduce and use NSThread, and we will continue to explain and use 2 and 3 later.

1.2 The shortcomings of the three methods are introduced:

NSThread:

Advantages: NSThread is lighter than the other two

Disadvantages: You need to manage the life cycle of threads yourself, and threads are synchronized. Thread synchronization will have a certain system overhead for data locking

There are three technologies implemented by NSThread:

TechnologyDescription
Cocoa threadsCocoa implements threads using the 
NSThread
 class. Cocoa also provides methods on 
NSObject
 for spawning new threads and executing code on already-running threads. For more information, see  Using NSThread  and  Using NSObject to Spawn a Thread.
POSIX threadsPOSIX threads provide a C-based interface for creating threads. If you are not writing a Cocoa application, this is the best choice for creating threads. The POSIX interface is relatively simple to use and offers ample flexibility for configuring your threads. For more information , see  Using POSIX Threads
Multiprocessing ServicesMultiprocessing Services is a legacy C-based interface used by applications transitioning from older versions of Mac OS. This technology is available in OS X only and should be avoided for any new development. Instead, you should use the 
NSThread
 class or POSIX threads. If you need more information on this technology, see  Multiprocessing Services Programming Guide .

Generally use cocoa thread technology.

\

Cocoa operation

Advantages: You don't need to care about thread management, data synchronization, and you can focus on the operations you need to perform.

Cocoa operation related classes are NSOperation and NSOperationQueue. NSOperation is an abstract class. To use it, you must use its subclasses. You can implement it or use two subclasses defined by it: NSInvocationOperation and NSBlockOperation. Create an object of the NSOperation subclass, and add the object to the NSOperationQueue queue for execution./

GCD

Grand Central Dispatch (GCD) is a multi-core programming solution developed by Apple. It can be used after iOS4.0 starts. GCD is a very efficient and powerful technology that replaces technologies such as NSThread, NSOperationQueue, NSInvocationOperation, etc. The current iOS system has been upgraded to 6, so don't worry about the technology not being used./

\

After introducing these three multi-threaded programming methods, we first introduce the use of NSThread in this article.

2. The use of NSThread

2.1 NSThread has two direct creation methods:

-(id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument

+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument

The first is an instance method, the second is a class method

[cpp]   view plain copy

  1. 1. [NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];  
  2. 2. NSThread* myThread = [[NSThread alloc] initWithTarget:self  
  3.                                         selector:@selector(doSomething:)  
  4.                                         object:nil];  
  5. [myThread start];  

2.2 The meaning of the parameters:

selector: The method of thread execution. This selector can only have one parameter and cannot have a return value.

target: the object sent by the selector message

argument: the only parameter transmitted to the target, it can also be nil

The first method will directly create a thread and start to run the thread. The second method is to create the thread object first, and then run the thread operation. You can set the thread priority and other thread information before running the thread operation.

2.3 PS: The method of not explicitly creating a thread:

Use the NSObject class method performSelectorInBackground:withObject: to create a thread:
[Obj performSelectorInBackground:@selector(doSomething) withObject:nil];\

2.4 Examples of downloading pictures:

2.4.1 New singeView app

Create a new project and place an imageView control on the xib file. Hold down the control key and drag to viewControll

Create imageView IBOutlet in er.h file 

Implementation in ViewController.m:

[cpp]   view plain copy

  1. //  
  2. //ViewController.m  
  3. //NSThreadDemo  
  4. //  
  5. //Created by rongfzh on 12-9-23.  
  6. //Copyright (c) 2012 rongfzh. All rights reserved.  
  7. //  
  8.   
  9. #import "ViewController.h"  
  10. #define kURL @" avatar.csdn.net/2/C/D/1_tot...
  11. @interface ViewController ()  
  12.   
  13. @end  
  14.   
  15. @implementation ViewController  
  16.   
  17. -(void)downloadImage:(NSString *) url{  
  18.     NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];  
  19.     UIImage *image = [[UIImage alloc]initWithData:data];  
  20.     if(image == nil){  
  21.           
  22.     }else{  
  23.         [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];  
  24.     }  
  25. }  
  26.   
  27. -(void)updateUI:(UIImage*) image{  
  28.     self.imageView.image = image;  
  29. }  
  30.   
  31.   
  32. -(void)viewDidLoad  
  33. {  
  34.     [super viewDidLoad];  
  35.       
  36. //[NSThread detachNewThreadSelector:@selector(downloadImage:) toTarget:self withObject:kURL];  
  37.     NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage:) object:kURL];  
  38.     [thread start];  
  39. }  
  40.   
  41. -(void)didReceiveMemoryWarning  
  42. {  
  43.     [super didReceiveMemoryWarning];  
  44.     //Dispose of any resources that can be recreated.  
  45. }  
  46.   
  47. @end  

2.4.2 Communication between threads

How does the thread notify the main thread to update the interface after downloading the picture?

[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];

performSelectorOnMainThread is a method of NSObject. In addition to updating the data of the main thread, you can also update other threads such as:

Use: performSelector:onThread:withObject:waitUntilDone: 

Run to download pictures:

\

The picture is downloaded.

2.3 Thread synchronization

We demonstrate a classic example of selling tickets to talk about the thread synchronization of NSThread:

.h

[cpp]   view plain copy

  1. #import <UIKit/UIKit.h>  
  2.   
  3. @class ViewController;  
  4.   
  5. @interface AppDelegate: UIResponder <UIApplicationDelegate>  
  6. {  
  7.     int tickets;  
  8.     int count;  
  9.     NSThread* ticketsThreadone;  
  10.     NSThread* ticketsThreadtwo;  
  11.     NSCondition* ticketsCondition;  
  12.     NSLock *theLock;  
  13. }  
  14. @property (strong, nonatomic) UIWindow *window;  
  15.   
  16. @property (strong, nonatomic) ViewController *viewController;  
  17.   
  18. @end  

[cpp]   view plain copy

  1. -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  
  2. {  
  3.       
  4.     tickets = 100;  
  5.     count = 0;  
  6.     theLock = [[NSLock alloc] init];  
  7.     //lock object  
  8.     ticketsCondition = [[NSCondition alloc] init];  
  9.     ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  10.     [ticketsThreadone setName:@"Thread-1"];  
  11.     [ticketsThreadone start];  
  12.       
  13.       
  14.     ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  15.     [ticketsThreadtwo setName:@"Thread-2"];  
  16.     [ticketsThreadtwo start];  
  17.       
  18.     self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];  
  19.     //Override point for customization after application launch.  
  20.     self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];  
  21.     self.window.rootViewController = self.viewController;  
  22.     [self.window makeKeyAndVisible];  
  23.     return YES;  
  24. }  
  25.   
  26. -(void)run{  
  27.     while (TRUE) {  
  28.         //locked  
  29. //[ticketsCondition lock];  
  30.         [theLock lock];  
  31.         if(tickets >= 0){  
  32.             [NSThread sleepForTimeInterval:0.09];  
  33.             count = 100-tickets;  
  34.             NSLog(@"The current number of votes is:%d,sold:%d,thread name:%@",tickets,count,[[NSThread currentThread] name]);  
  35.             tickets--;  
  36.         }else{  
  37.             break;  
  38.         }  
  39.         [theLock unlock];  
  40. //[ticketsCondition unlock];  
  41.     }  
  42. }  

If there is no thread synchronized lock, the number of tickets sold may be -1. After the lock is added, the thread synchronization ensures the correctness of the data.
In the above example, I used two locks, one NSCondition and the other: NSLock. I have annotated NSCondition.

Sequential execution of threads

They can all pass

        [ticketsCondition signal]; The method of sending a signal is to wake up the waiting of another thread in one thread.

such as:

[cpp]   view plain copy

  1. #import "AppDelegate.h"  
  2.   
  3. #import "ViewController.h"  
  4.   
  5. @implementation AppDelegate  
  6.   
  7. -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  
  8. {  
  9.       
  10.     tickets = 100;  
  11.     count = 0;  
  12.     theLock = [[NSLock alloc] init];  
  13.     //lock object  
  14.     ticketsCondition = [[NSCondition alloc] init];  
  15.     ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  16.     [ticketsThreadone setName:@"Thread-1"];  
  17.     [ticketsThreadone start];  
  18.       
  19.     ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  20.     [ticketsThreadtwo setName:@"Thread-2"];  
  21.     [ticketsThreadtwo start];  
  22.       
  23.     NSThread *ticketsThreadthree = [[NSThread alloc] initWithTarget:self selector:@selector(run3) object:nil];  
  24.     [ticketsThreadthree setName:@"Thread-3"];  
  25.     [ticketsThreadthree start];      
  26.     self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];  
  27.     //Override point for customization after application launch.  
  28.     self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];  
  29.     self.window.rootViewController = self.viewController;  
  30.     [self.window makeKeyAndVisible];  
  31.     return YES;  
  32. }  
  33.   
  34. -(void)run3{  
  35.     while (YES) {  
  36.         [ticketsCondition lock];  
  37.         [NSThread sleepForTimeInterval:3];  
  38.         [ticketsCondition signal];  
  39.         [ticketsCondition unlock];  
  40.     }  
  41. }  
  42.   
  43. -(void)run{  
  44.     while (TRUE) {  
  45.         //locked  
  46.         [ticketsCondition lock];  
  47.         [ticketsCondition wait];  
  48.         [theLock lock];  
  49.         if(tickets >= 0){  
  50.             [NSThread sleepForTimeInterval:0.09];  
  51.             count = 100-tickets;  
  52.             NSLog(@"The current number of votes is:%d,sold:%d,thread name:%@",tickets,count,[[NSThread currentThread] name]);  
  53.             tickets--;  
  54.         }else{  
  55.             break;  
  56.         }  
  57.         [theLock unlock];  
  58.         [ticketsCondition unlock];  
  59.     }  
  60. }  

Wait is waiting. I added a thread 3 to wake up the wait in the lock of the other two threads.

Other synchronization

We can use the instruction @synchronized to simplify the use of NSLock, so that we don't have to display the creation of NSLock, lock and unlock related code.
-(void)doSomeThing:(id)anObj
{
@synchronized(anObj)
{
//Everything between the braces is protected by the @synchronized directive.
}
}
There are other lock objects, such as: cyclic lock NSRecursiveLock, conditional lock NSConditionLock , Distributed lock NSDistributedLock, etc., you can read the official documents to learn by yourself\

Sample code for NSThread to download pictures: download.csdn.net/detail/toto...

Copyright statement: This article was originally created by blog.csdn.net/totogo2010/ , welcome to reprint and share. Please respect the author s labor and keep the statement and the author s blog link when reprinting, thank you!

\

Appendix 1: How to update the main thread UI in the child thread

In iphone development, it is often necessary to update the display of the UI. Generally, a new thread needs to be started to update the relevant data, and then the UI is updated in another thread. Here, NSThread is used to implement such a function: update the progress bar.
Copy the code
//
//NSThreadDemoAppDelegate.m
//NSThreadDemo
//
//Created by Chelsea Wang(420989762/wwssttt@163.com) on 11-10-11.
//Copyright 2011 __MyCompanyName__. All rights reserved.
//

@implementation NSThreadDemoAppDelegate
float processValue = 0;
@synthesize window = _window;-(

BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//Override point for customization after application launch.
UIProgressView* processView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
[processView setFrame:CGRectMake(10, 50, 200, 30)];
[processView setTag:101];
[processView setProgress:0.0];

UILabel* processLabel = [[UILabel alloc] initWithFrame:CGRectMake(225, 30, 100, 50 )];
[processLabel setText:[NSString stringWithFormat:@"%.2f%%",processValue*100]];
[processLabel setTag:102];

[self.window addSubview:processView];
[processView release];
[self. window addSubview:processLabel];
[processLabel release];
[self.window makeKeyAndVisible];

[NSThread detachNewThreadSelector:@selector(updateProcess) toTarget:self withObject:nil];
return YES;
}

-(void)updateProcess{
NSAutoreleasePool* p = [ [NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:YES];
[p release];
}
-(void)updateUI{
if (processValue <= 1.0) {
processValue += 0.1;

UIProgressView* processView = (UIProgressView*) [self.window viewWithTag:101];
[processView setProgress:processValue];

UILabel* processLabel = (UILabel*)[self.window viewWithTag:102];
[processLabel setText:[NSString stringWithFormat:@"%.2f%%", processValue*100]];

[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateUI) userInfo:nil repeats:NO];
}else{
processValue = 0.0;
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateUI) userInfo:nil repeats:NO];
}
}\

\

Appendix 2: Inter-thread communication

The thread may need to communicate with other threads while it is running. We can apply some methods
in NSObject: do work in the main thread of the application:
performSelectorOnMainThread:withObject:waitUntilDone:
performSelectorOnMainThread:withObject:waitUntilDone:modes:

do work in the specified thread:
performSelector:onThread:withObject:waitUntilDone:
performSelector:onThread : withObject: waitUntilDone: modes:

to do the work in the current thread:
performSelector: withObject: afterDelay:
performSelector: withObject: afterDelay: inModes:

void a message to the current thread:
cancelPreviousPerformRequestsWithTarget:
cancelPreviousPerformRequestsWithTarget: or: Object:

as we After completing the data in a thread, you need to notify the main thread to update the interface, etc., you can use the following interface:

-(Void)myThreadMainMethod
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//to do something in your thread job
...
[self performSelectorOnMainThread: or(UI) withObject:nil waitUntilDone:NO];
[pool release ];
}


Appendix III.
When RunLoop talks about NSThread , it is impossible not to talk about NSRunLoop, which is closely related to it. Run loop is equivalent to the message cycle mechanism in win32, which allows you to adjust whether the thread is busy or idle according to the transaction/message (mouse message, keyboard message, timer message, etc.). The system will actively generate a corresponding run loop for the main thread of the application program to deal with its message rounds. The reason why touchesBegan/touchesMoved and other functions can be called when touching UIView is because the main thread of the application has such a run loop in UIApplicationMain to distribute input or timer transactions./

1. What is NSRunLoop?

We will often see code like this:

-(IBAction)start:(id)sender

{

pageStillLoading = YES;

[NSThread detachNewThreadSelector:@selector(loadPageInBackground:)toTarget:self withObject:nil];

[progress setHidden:NO];

while (pageStillLoading) {

[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

}

[progress setHidden:YES];

}

This code is amazing, because it "pauses" the code running, and the program running will not be affected by a while loop here. After the execution of [progress setHidden:NO], the whole function stops in the loop as if it wants to pause, and then let [progress setHidden:YES] run after the operations in loadPageInBackground are completed. This is very brief and the logic is very clear. If you don't do this, you need to call [progress setHidden:YES] where the load is complete in loadPageInBackground, which is not compact and error-prone.

So what exactly is NSRunLoop? In fact, the essence of NSRunLoop is a processing mode of a message mechanism. If you have a certain understanding of vc++ programming, in windows, there are a series of very important functions SendMessage, PostMessage, GetMessage, these are APIs related to message passing processing. But when you enter the programming world of Cocoa, I don t know if you walked too fast and in a hurry and ignored this very important issue. Cocoa didn t mention any API about message processing, and the developer never mentioned it. Haven't I ever cared about the process of message delivery, as if everything is so natural, as natural as nature? In Cocoa, you no longer need to define macros like WM_COMMAD_XXX to identify a certain message, and you don t need to do special processing on specific messages in switch-case. Is there no message mechanism in Cocoa? The answer is no, but Apple has adopted a more sophisticated model when designing message processing, which is RunLoop.

2. NSRunLoop working principle

Next, let's take a look at the specific working principle of NSRunLoop. The first is the statement provided by the official document, see the picture:

\

Through all the "messages" have been added to NSRunLoop, and here these messages are divided into "input source" and "Timer source" and check in the loop whether there is an event that needs to happen, if necessary, then call the corresponding Function processing. In order to explain more clearly, let's compare the message processing process between VC++ and iOS.

In VC++, after all the initialization is completed, the program starts such a loop (the code is intercepted from the slides of the sir mfc programming course):

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){

...

while (GetMessage(&msg, NULL, 0, 0)){

if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)){

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

}

It can be seen that the message is distributed and processed after GetMessage, and the main function in iOS only calls UIApplicationMain, then we can mind guessing that UIApplicationMain will enter such a situation after initialization:

int UIApplicationMain(...){

...

while(running){

[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

}

...

}

Therefore, in UIApplicationMain, the program that is constantly processing runloop does not exit. Just now I said that NSRunLoop is a more sophisticated message processing mode. He is smart in abstracting and encapsulating the message processing process, so that you don t have to deal with some very trivial and low-level specific messages. Processing, each message in NSRunLoop is packaged in the input source or timer source, and when it needs to be processed, the processing function of the corresponding object contained in it is directly called. So for external developers, what you feel is that the source/timer is added to the runloop, and then something like [receiver action] happens when appropriate. Even many times, you don't feel the first half of the whole process, you just feel that a certain function of one of your objects is called. For example, when the UIView is touched, functions such as touchesBegan/touchesMoved will be called. Maybe you think, "Damn, I didn't know there was a touch message there, so these processing functions were called!?" So, The news is there, but runloop has done it for you! To prove my point, I intercepted a call stack of debug touchesBegan. There are pictures and the truth:

\

 

Now I will look at the example of the "pause" code just now. Do you have a deeper understanding of it?