天天看點

iphone 線程總結— detachNewThreadSelector的使用

不管是iphone中還是其他的作業系統,多線程在各種程式設計語言中都是難點,很多語言中實作起來很麻煩,objective-c雖然源于c,但其多線程程式設計卻相當簡單,可以與java相媲美。多線程程式設計是防止主線程堵塞,增加運作效率等等的最佳方法。而原始的多線程方法存在很多的毛病,包括線程鎖死等。

一、線程建立與啟動

線程建立主要有二種方式:

  1. (id)init; // designated initializer  
  2. (id)initWithTarget:(id)target selector:  
  3. (SEL)selector object:(id)argument; 

當然,還有一種比較特殊,就是使用所謂的convenient method,這個方法可以直接生成一個線程并啟動它,而且無需為線程的清理負責。這個方法的接口是:

  1. (void)detachNewThreadSelector:  
  2. (SEL)aSelector toTarget:  
  3. (id)aTarget withObject:  
  4. (id)anArgument 

前兩種方法建立後,需要手機啟動,啟動的方法是:

  1. (void)start; 

二、線程的同步與鎖

要說明線程的同步與鎖,最好的例子可能就是多個視窗同時售票的售票系統了。我們知道在java中,使用synchronized來同步,而iphone雖然沒有提供類似java下的synchronized關鍵字,但提供了NSCondition對象接口。檢視NSCondition的接口說明可以看出,NSCondition是iphone下的鎖對象,是以我們可以使用NSCondition實作iphone中的線程安全。這是來源于網上的一個例子:

SellTicketsAppDelegate.h 檔案

  1. // SellTicketsAppDelegate.h  
  2. import <UIKit/UIKit.h> 
  3. @interface SellTicketsAppDelegate : NSObject <UIApplicationDelegate> {  
  4.      int tickets;  
  5.      int count;  
  6.      NSThread* ticketsThreadone;  
  7.      NSThread* ticketsThreadtwo;  
  8.      NSCondition* ticketsCondition;  
  9.      UIWindow *window;  
  10. }  
  11. @property (nonatomic, retain) IBOutlet UIWindow *window;  
  12. @end  
  13. SellTicketsAppDelegate.m 檔案  
  14. // SellTicketsAppDelegate.m  
  15. import "SellTicketsAppDelegate.h"  
  16. @implementation SellTicketsAppDelegate  
  17. @synthesize window;  
  18. - (void)applicationDidFinishLaunching:(UIApplication *)application {  
  19.      tickets = 100;  
  20.      count = 0;  
  21.      // 鎖對象  
  22.      ticketCondition = [[NSCondition alloc] init];  
  23.      ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  24.      [ticketsThreadone setName:@"Thread-1"];  
  25.      [ticketsThreadone start];  
  26.      ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  27.      [ticketsThreadtwo setName:@"Thread-2"];  
  28.      [ticketsThreadtwo start];  
  29.      //[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];  
  30.       // Override point for customization after application launch  
  31.      [window makeKeyAndVisible];  
  32. }  
  33. - (void)run{  
  34.      while (TRUE) {  
  35.      // 上鎖  
  36.          [ticketsCondition lock];  
  37.          if(tickets > 0){  
  38.              [NSThread sleepForTimeInterval:0.5];  
  39.              count = 100 - tickets;  
  40.              NSLog(@"目前票數是:%d,售出:%d,線程名:%@",tickets,count,[[NSThread currentThread] name]);  
  41.              tickets--;  
  42.          }else{  
  43.              break;  
  44.          }  
  45.          [ticketsCondition unlock];  
  46.      }  
  47. }  
  48. - (void)dealloc {  
  49. [ticketsThreadone release];  
  50.      [ticketsThreadtwo release];  
  51.      [ticketsCondition release];   
  52.      [window release];  
  53.      [super dealloc];  
  54. }  
  55. @end 

三、線程的互動

線程在運作過程中,可能需要與其它線程進行通信,如在主線程中修改界面等等,可以使用如下接口:

  1. (void)performSelectorOnMainThread:  
  2. (SEL)aSelector withObject:  
  3. (id)arg waitUntilDone:  
  4. (BOOL)wait 

由于在本過程中,可能需要釋放一些資源,則需要使用NSAutoreleasePool來進行管理,如:

  1. (void)startTheBackgroundJob {       
  2. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
  3.     // to do something in your thread job  
  4.     ...  
  5.     [self performSelectorOnMainThread:@selector(makeMyProgressBarMoving) withObject:nil waitUntilDone:NO];  
  6.     [pool release];  
舉例說明怎麼簡單的建立一個子線程。 用到的類是NSThread類,這裡使用detachNewTheadSelector:toTagaet:withObject建立一個線程。 函數setupThread:(NSArray*)userInfor。通過userInfor将需要的資料傳到線程中。 函數定義:

[代碼]c#/cpp/oc代碼:

01

-(

void

)setupThread:(NSArray*)userInfor{

02

03

[NSThread detachNewThreadSelector:@selector(threadFunc:) toTarget:self withObject:(id)userInfor];

04

05

}

06

07

- (

void

)threadFunc:(id)userInfor{

08

09

NSAutoreleasePool*pool = [[NSAutoreleasePool alloc] init];

10

11

//。。。。需要做的處理。

12

13

//這裡線程結束後立即傳回

14

15

[self performSelectorOnMainThread:@selector(endThread) withObject:nil waitUntilDone:NO];

16

17

[pool release];

18

19

}

performSelectorOnMainThread通知主線程執行函數endThread。也可以使用performSelector:onThread:withObject:waitUntil 通知某線程執行線程結束後的處理。

線程内不要重新整理界面。如果需要重新整理界面,通過performSelectorOnMainThread,調出主線程中的方法去重新整理。

例如,啟動一個線程下載下傳圖檔:

//啟動線程

[NSThread detachNewThreadSelector:@selector(downloadImage:) toTarget:self withObject:url];

//線程函數

[代碼]c#/cpp/oc代碼:

01

- (

void

) downloadImage:(NSString*)url{

02

03

_subThreed = [NSThread currentThread];

04

05

self.uploadPool = [[NSAutoreleasePool alloc] init];

06

self.characterBuffer = [NSMutableData data];

07

done = NO;

08

[[NSURLCache sharedURLCache] removeAllCachedResponses];

09

10

NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:[NSURLURLWithString:url]];

11

12

self.connection = [[NSURLConnection alloc] initWithRequest:theRequest 

delegate

:self];

13

[self performSelectorOnMainThread:@selector(httpConnectStart) withObject:nil waitUntilDone:NO];

14

if

(connection != nil) {

15

do

{

16

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

17

while

(!done);

18

}

19

20

self.photo = [UIImage imageWithData:characterBuffer];

21

22

23

//下載下傳結束,重新整理

24

[self performSelectorOnMainThread:@selector(fillPhoto) withObject:nil waitUntilDone:NO];

25

26

// Release resources used only in this thread.

27

self.connection = nil;

28

[uploadPool release];

29

self.uploadPool = nil;

30

31

_subThreed = nil;

32

}

33

34

35

36

#pragma mark NSURLConnection Delegate methods

37

38

/*

39

Disable caching so that each time we run this app we are starting with a clean slate. You may not want to do this in your application.

40

*/

41

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {

42

43

return

nil;

44

}

45

46

// Forward errors to the delegate.

47

- (

void

)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {

48

done = YES;

49

[self performSelectorOnMainThread:@selector(httpConnectEnd) withObject:nil waitUntilDone:NO];

50

[characterBuffer setLength:0];

51

52

}

53

54

// Called when a chunk of data has been downloaded.

55

- (

void

)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {

56

// Process the downloaded chunk of data.

57

58

[characterBuffer appendData:data];

59

60

}

61

62

- (

void

)connectionDidFinishLoading:(NSURLConnection *)connection {

63

64

[self performSelectorOnMainThread:@selector(httpConnectEnd) withObject:nil waitUntilDone:NO];

65

// Set the condition which ends the run loop.

66

done = YES;

67

68

}

首先我們需要建立一個線程有兩種方法:

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

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

因為第二種方法不用對線程進行清理,是以我們常用第二種哦個方法。

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

- (void)new:(id)sender{

[_myCondition lock];

//performSelectorInBackgroud主要進行邏輯上處理

[self performSelectorInBackgroud:@selector(doInBackgroud:) withObject:nil];

//perfomSelectorOnMainThread主要進行界面UI上的處理

[self perfomSelectorOnMainThread:@selector(doOnMain:) withObject:nil];

//[NSThread sleepForTimeInterval:n];

[_myCondition signal];

[_myCondition unlock];

[NSThread exit];

return;

}

繼續閱讀