天天看點

iOS多線程程式設計之NSThread的使用

這三種程式設計方式從上到下,抽象度層次是從低到高的,抽象度越高的使用越簡單,也是Apple最推薦使用的。

這篇我們主要介紹和使用NSThread,後面會繼續2、3 的講解和使用。

NSThread:

優點:NSThread 比其他兩個輕量級

缺點:需要自己管理線程的生命周期,線程同步。線程同步對資料的加鎖會有一定的系統開銷

NSThread實作的技術有下面三種:

Technology

Description

Cocoa threads

POSIX threads

POSIX 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.

Multiprocessing Services

Multiprocessing 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 <code>NSThread</code> class

or POSIX threads. If you need more information on this technology, see Multiprocessing Services Programming Guide.

一般使用cocoa thread 技術。

Cocoa operation 

優點:不需要關心線程管理,資料同步的事情,可以把精力放在自己需要執行的操作上。

Cocoa operation 相關的類是 NSOperation ,NSOperationQueue。NSOperation是個抽象類,使用它必須用它的子類,可以實作它或者使用它定義好的兩個子類:NSInvocationOperation 和 NSBlockOperation。建立NSOperation子類的對象,把對象添加到NSOperationQueue隊列裡執行。

GCD

Grand Central Dispatch (GCD)是Apple開發的一個多核程式設計的解決方法。在iOS4.0開始之後才能使用。GCD是一個替代諸如NSThread, NSOperationQueue, NSInvocationOperation等技術的很高效和強大的技術。現在的iOS系統都更新到6了,是以不用擔心該技術不能使用。

介紹完這三種多線程程式設計方式,我們這篇先介紹NSThread的使用。

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

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

第一個是執行個體方法,第二個是類方法

1、[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];  

2、NSThread* myThread = [[NSThread alloc] initWithTarget:self  

                                        selector:@selector(doSomething:)  

                                        object:nil];  

[myThread start];  

selector :線程執行的方法,這個selector隻能有一個參數,而且不能有傳回值。

target  :selector消息發送的對象

argument:傳輸給target的唯一參數,也可以是nil

第一種方式會直接建立線程并且開始運作線程,第二種方式是先建立線程對象,然後再運作線程操作,在運作線程操作前可以設定線程的優先級等線程資訊

用NSObject的類方法  performSelectorInBackground:withObject: 建立一個線程:

[Obj performSelectorInBackground:@selector(doSomething) withObject:nil];

建立項目,并在xib檔案上放置一個imageView控件。按住control鍵拖到viewControll

er.h檔案中建立imageView IBOutlet 

ViewController.m中實作:

//  

//  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  

-(void)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];  

    }  

}  

-(void)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.  

線程下載下傳完圖檔後怎麼通知主線程更新界面呢?

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

performSelectorOnMainThread是NSObject的方法,除了可以更新主線程的資料外,還可以更新其他線程的比如:

用:performSelector:onThread:withObject:waitUntilDone: 

運作下載下傳圖檔:

圖檔下載下傳下來了。

我們示範一個經典的賣票的例子來講NSThread的線程同步:

.h

#import &lt;UIKit/UIKit.h&gt;  

@class ViewController;  

@interface AppDelegate : UIResponder &lt;UIApplicationDelegate&gt;  

    int tickets;  

    int count;  

    NSThread* ticketsThreadone;  

    NSThread* ticketsThreadtwo;  

    NSCondition* ticketsCondition;  

    NSLock *theLock;  

@property (strong, nonatomic) UIWindow *window;  

@property (strong, nonatomic) ViewController *viewController;  

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  

    tickets = 100;  

    count = 0;  

    theLock = [[NSLock alloc] init];  

    // 鎖對象  

    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 &gt;= 0){  

            [NSThread sleepForTimeInterval:0.09];  

            count = 100 - tickets;  

            NSLog(@"目前票數是:%d,售出:%d,線程名:%@",tickets,count,[[NSThread currentThread] name]);  

            tickets--;  

        }else{  

            break;  

        }  

        [theLock unlock];  

//        [ticketsCondition unlock];  

如果沒有線程同步的lock,賣票數可能是-1.加上lock之後線程同步保證了資料的正确性。

上面例子我使用了兩種鎖,一種NSCondition ,一種是:NSLock。 NSCondition我已經注釋了。

他們都可以通過

        [ticketsCondition signal]; 發送信号的方式,在一個線程喚醒另外一個線程的等待。

比如:

#import "AppDelegate.h"  

@implementation AppDelegate  

    NSThread *ticketsThreadthree = [[NSThread alloc] initWithTarget:self selector:@selector(run3) object:nil];  

    [ticketsThreadthree setName:@"Thread-3"];  

    [ticketsThreadthree start];      

-(void)run3{  

    while (YES) {  

        [ticketsCondition lock];  

        [NSThread sleepForTimeInterval:3];  

        [ticketsCondition signal];  

        [ticketsCondition unlock];  

        [ticketsCondition wait];  

wait是等待,我加了一個 線程3 去喚醒其他兩個線程鎖中的wait

我們可以使用指令 @synchronized 來簡化 NSLock的使用,這樣我們就不必顯示編寫建立NSLock,加鎖并解鎖相關代碼。

- (void)doSomeThing:(id)anObj

{

    @synchronized(anObj)

    {

        // Everything between the braces is protected by the @synchronized directive.

    }

}

還有其他的一些鎖對象,比如:循環鎖NSRecursiveLock,條件鎖NSConditionLock,分布式鎖NSDistributedLock等等,可以自己看官方文檔學習