iOS多線程編程之NSThread的使用
1、簡(jiǎn)介:
1.1 iOS有三種多線程編程的技術(shù),分別是:
1.、NSThread
2、Cocoa NSOperation (iOS多線程編程之NSOperation和NSOperationQueue的使用)
3、GCD 全稱:Grand Central Dispatch( iOS多線程編程之Grand Central Dispatch(GCD)介紹和使用)
這三種編程方式從上到下,抽象度層次是從低到高的,抽象度越高的使用越簡(jiǎn)單,也是Apple最推薦使用的。
這篇我們主要介紹和使用NSThread,后面會(huì)繼續(xù)2、3 的講解和使用。
1.2 三種方式的優(yōu)缺點(diǎn)介紹:
NSThread:
優(yōu)點(diǎn):NSThread 比其他兩個(gè)輕量級(jí)
缺點(diǎn):需要自己管理線程的生命周期,線程同步。線程同步對(duì)數(shù)據(jù)的加鎖會(huì)有一定的系統(tǒng)開銷
NSThread實(shí)現(xiàn)的技術(shù)有下面三種:
Cocoa threads
POSIX threads
Multiprocessing Services
一般使用cocoa thread 技術(shù)。
Cocoa operation
優(yōu)點(diǎn):不需要關(guān)心線程管理,數(shù)據(jù)同步的事情,可以把精力放在自己需要執(zhí)行的操作上。
Cocoa operation 相 關(guān)的類是 NSOperation ,NSOperationQueue。NSOperation是個(gè)抽象類,使用它必須用它的子類,可以實(shí)現(xiàn)它或者使用 它定義好的兩個(gè)子類:NSInvocationOperation 和 NSBlockOperation。創(chuàng)建NSOperation子類的對(duì)象,把對(duì) 象添加到NSOperationQueue隊(duì)列里執(zhí)行。
GCD
Grand Central Dispatch (GCD) 是Apple開發(fā)的一個(gè)多核編程的解決方法。在iOS4.0開始之后才能使用。GCD是一個(gè)替代諸如 NSThread, NSOperationQueue, NSInvocationOperation等技術(shù)的很高效和強(qiáng)大的技術(shù)。現(xiàn)在的iOS系統(tǒng)都 升級(jí)到6了,所以不用擔(dān)心該技術(shù)不能使用。
介紹完這三種多線程編程方式,我們這篇先介紹NSThread的使用。
2、NSThread的使用
2.1 NSThread 有兩種直接創(chuàng)建方式:
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument
第一個(gè)是實(shí)例方法,第二個(gè)是類方法
- [NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];
- NSThread* myThread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:) object:nil];
- [myThread start];
2.2參數(shù)的意義:
selector :線程執(zhí)行的方法,這個(gè)selector只能有一個(gè)參數(shù),而且不能有返回值。
target :selector消息發(fā)送的對(duì)象
argument:傳輸給target的唯一參數(shù),也可以是nil
第一種方式會(huì)直接創(chuàng)建線程并且開始運(yùn)行線程,第二種方式是先創(chuàng)建線程對(duì)象,然后再運(yùn)行線程操作,在運(yùn)行線程操作前可以設(shè)置線程的優(yōu)先級(jí)等線程信息
2.3 PS:不顯式創(chuàng)建線程的方法:
用NSObject的類方法 performSelectorInBackground:withObject: 創(chuàng)建一個(gè)線程:
- [Obj performSelectorInBackground:@selector(doSomething) withObject:nil];
2.4 下載圖片的例子:
2.4.1 新建singeView app
新建項(xiàng)目,并在xib文件上放置一個(gè)imageView控件。按住control鍵拖到viewController.h文件中創(chuàng)建imageView IBOutlet ViewController.m中實(shí)現(xiàn):
- // ViewController.m
- // NSThreadDemo
- //
- // Created by rongfzh on 12-9-23.
- // Copyright (c) 2012年 rongfzh. All rights reserved.
- //
- #import "ViewController.h"
- #define kURL @"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"
- @interface ViewController ()
- @end
- @implementation ViewController
- d)downloadImage:(NSString *) url{
- NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
- UIImage *image = [[UIImage alloc]initWithData:data];
- if(image == nil){
- }else{
- [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
- }
- }
- d)updateUI:(UIImage*) image{
- self.imageView.image = image;
- }
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- // [NSThread detachNewThreadSelector:@selector(downloadImage:) toTarget:self withObject:kURL];
- NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage:) object:kURL];
- [thread start];
- }
- - (void)didReceiveMemoryWarning
- {
- [super didReceiveMemoryWarning];
- // Dispose of any resources that can be recreated.
- }
- @end
2.4.2線程間通訊
線程下載完圖片后怎么通知主線程更新界面呢?
- [selfperformSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
performSelectorOnMainThread是NSObject的方法,除了可以更新主線程的數(shù)據(jù)外,還可以更新其他線程的比如:
用:performSelector:onThread:withObject:waitUntilDone:
運(yùn)行下載圖片:
圖片下載下來了。
2.3 線程同步
我們演示一個(gè)經(jīng)典的賣票的例子來講NSThread的線程同步:
.h
- #import <UIKit/UIKit.h>
- @class ViewController;
- @interface AppDelegate : UIResponder <UIApplicationDelegate>
- {
- int tickets;
- int count;
- NSThread* ticketsThreadone;
- NSThread* ticketsThreadtwo;
- NSCondition* ticketsCondition;
- NSLock *theLock;
- }
- @property (strong, nonatomic) UIWindow *window;
- @property (strong, nonatomic) ViewController *viewController;
- @end
- - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- {
- tickets = 100;
- count = 0;
- theLock = [[NSLock alloc] init];
- // 鎖對(duì)象
- ticketsCondition = [[NSCondition alloc] init];
- ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
- [ticketsThreadone setName:@"Thread-1"];
- [ticketsThreadone start];
- ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
- [ticketsThreadtwo setName:@"Thread-2"];
- [ticketsThreadtwo start];
- self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
- // Override point for customization after application launch.
- self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
- self.window.rootViewController = self.viewController;
- [self.window makeKeyAndVisible];
- return YES;
- }
- - (void)run{
- while (TRUE) {
- // 上鎖
- // [ticketsCondition lock];
- [theLock lock];
- if(tickets >= 0){
- [NSThread sleepForTimeInterval:0.09];
- count = 100 - tickets;
- NSLog(@"當(dāng)前票數(shù)是:%d,售出:%d,線程名:%@",tickets,count,[[NSThread currentThread] name]);
- tickets--;
- }else{
- break;
- }
- [theLock unlock];
- // [ticketsCondition unlock];
- }
- }
如果沒有線程同步的lock,賣票數(shù)可能是-1.加上lock之后線程同步保證了數(shù)據(jù)的正確性。
上面例子我使用了兩種鎖,一種NSCondition ,一種是:NSLock。 NSCondition我已經(jīng)注釋了。
線程的順序執(zhí)行
他們都可以通過
[ticketsConditionsignal]; 發(fā)送信號(hào)的方式,在一個(gè)線程喚醒另外一個(gè)線程的等待。
比如:
- #import "AppDelegate.h"
- #import "ViewController.h"
- @implementation AppDelegate
- - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- {
- tickets = 100;
- count = 0;
- theLock = [[NSLock alloc] init];
- // 鎖對(duì)象
- ticketsCondition = [[NSCondition alloc] init];
- ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
- [ticketsThreadone setName:@"Thread-1"];
- [ticketsThreadone start];
- ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
- [ticketsThreadtwo setName:@"Thread-2"];
- [ticketsThreadtwo start];
- NSThread *ticketsThreadthree = [[NSThread alloc] initWithTarget:self selector:@selector(run3) object:nil];
- [ticketsThreadthree setName:@"Thread-3"];
- [ticketsThreadthree start];
- self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
- // Override point for customization after application launch.
- self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
- self.window.rootViewController = self.viewController;
- [self.window makeKeyAndVisible];
- return YES;
- }
- -(void)run3{
- while (YES) {
- [ticketsCondition lock];
- [NSThread sleepForTimeInterval:3];
- [ticketsCondition signal];
- [ticketsCondition unlock];
- }
- }
- - (void)run{
- while (TRUE) {
- // 上鎖
- [ticketsCondition lock];
- [ticketsCondition wait];
- [theLock lock];
- if(tickets >= 0){
- [NSThread sleepForTimeInterval:0.09];
- count = 100 - tickets;
- NSLog(@"當(dāng)前票數(shù)是:%d,售出:%d,線程名:%@",tickets,count,[[NSThread currentThread] name]);
- tickets--;
- }else{
- break;
- }
- [theLock unlock];
- [ticketsCondition unlock];
- }
- }
wait是等待,我加了一個(gè) 線程3 去喚醒其他兩個(gè)線程鎖中的wait
其他同步
我們可以使用指令@synchronized來簡(jiǎn)化 NSLock的使用,這樣我們就不必顯示編寫創(chuàng)建NSLock,加鎖并解鎖相關(guān)代碼。
- - (void)doSomeThing:(id)anObj
- {
- @synchronized(anObj)
- {
- // Everything between the braces is protected by the@synchronizeddirective.
- }
- }
還有其他的一些鎖對(duì)象,比如:循環(huán)鎖NSRecursiveLock,條件鎖NSConditionLock,分布式鎖NSDistributedLock等等,可以自己看官方文檔學(xué)習(xí)
NSThread下載圖片的例子代碼:http://download.csdn.net/detail/totogo2010/4591149