iOS開發:多線程編程之NSThread的使用詳解

來源:互聯網
上載者:User

   1、簡介:

  1.1 iOS有三種多線程編程的技術,分別是:

  1.、NSThread

  2、Cocoa NSOperation (iOS多線程編程之NSOperation和NSOperationQueue的使用)

  3、GCD 全稱:Grand Central Dispatch( iOS多線程編程之Grand Central Dispatch(GCD)介紹和使用)

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

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

  1.2 三種方式的優缺點介紹:

  NSThread:

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

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

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

  一般使用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的使用。

  2、NSThread的使用

  2.1 NSThread 有兩種直接建立方式:

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

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

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

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

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

  [myThread start];

  2.2參數的意義:

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

  target :selector訊息發送的對象

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

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

  2.3 PS:不顯式建立線程的方法:

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

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

  2.4 下載圖片的例子:

  2.4.1 建立singeView app

  建立項目,並在xib檔案上放置一個imageView控制項。按住control鍵拖到viewController.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

  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的方法,除了可以更新主線程的資料外,還可以更新其他線程的比如:

  用:performSelector:onThread:withObject:waitUntilDone:

  運行下載圖片:

  圖片下載下來了。

  2.3 線程同步

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

  .h

  #import

  @class ViewController;

  @interface AppDelegate : UIResponder

  {

  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];

  // 鎖對象

  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,售出:%d,線程名:%@",tickets,count,[[NSThread currentThread] name]);

  tickets--;

  }else{

  break;

  }

  [theLock unlock];

  // [ticketsCondition unlock];

  }

  }

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

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

  線程的順序執行

  他們都可以通過

  [ticketsConditionsignal]; 發送訊號的方式,在一個線程喚醒另外一個線程的等待。

  比如:

  #import "AppDelegate.h"

  #import "ViewController.h"

  @implementation AppDelegate

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

  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,售出:%d,線程名:%@",tickets,count,[[NSThread currentThread] name]);

  tickets--;

  }else{

  break;

  }

  [theLock unlock];

  [ticketsCondition unlock];

  }

  }

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

  其他同步

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

  - (void)doSomeThing:(id)anObj

  {

  @synchronized(anObj)

  {

  // Everything between the braces is protected by the@synchronizeddirective.

  }

  }

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

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

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.