不管是iphone中還是其他的操作系統,多線程在各種編程語言中都是難點,很多語言中實現起來很麻煩,objective-c雖然源於c,但其多線程編程卻相當簡單,可以與java相媲美。多線程編程是防止主線程堵塞,增加運行效率等等的最佳方法。而原始的多線程方法存在很多的毛病,包括線程鎖死等。
一、線程創建與啓動
線程創建主要有二種方式:
- (id)init; // designated initializer
- (id)initWithTarget:(id)target selector:
- (SEL)selector object:(id)argument;
當然,還有一種比較特殊,就是使用所謂的convenient method,這個方法可以直接生成一個線程並啓動它,而且無需爲線程的清理負責。這個方法的接口是:
- (void)detachNewThreadSelector:
- (SEL)aSelector toTarget:
- (id)aTarget withObject:
- (id)anArgument
前兩種方法創建後,需要手機啓動,啓動的方法是:
- (void)start;
二、線程的同步與鎖
要說明線程的同步與鎖,最好的例子可能就是多個窗口同時售票的售票系統了。我們知道在java中,使用synchronized來同步,而iphone雖然沒有提供類似java下的synchronized關鍵字,但提供了NSCondition對象接口。查看NSCondition的接口說明可以看出,NSCondition是iphone下的鎖對象,所以我們可以使用NSCondition實現iphone中的線程安全。這是來源於網上的一個例子:
SellTicketsAppDelegate.h 文件
- // SellTicketsAppDelegate.h
- import <UIKit/UIKit.h>
- @interface SellTicketsAppDelegate : NSObject <UIApplicationDelegate> {
- int tickets;
- int count;
- NSThread* ticketsThreadone;
- NSThread* ticketsThreadtwo;
- NSCondition* ticketsCondition;
- UIWindow *window;
- }
- @property (nonatomic, retain) IBOutlet UIWindow *window;
- @end
- SellTicketsAppDelegate.m 文件
- // SellTicketsAppDelegate.m
- import "SellTicketsAppDelegate.h"
- @implementation SellTicketsAppDelegate
- @synthesize window;
- - (void)applicationDidFinishLaunching:(UIApplication *)application {
- tickets = 100;
- count = 0;
- // 鎖對象
- ticketCondition = [[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 detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
- // Override point for customization after application launch
- [window makeKeyAndVisible];
-
- }
-
- - (void)run{
- while (TRUE) {
- // 上鎖
- [ticketsCondition lock];
- if(tickets > 0){
- [NSThread sleepForTimeInterval:0.5];
- count = 100 - tickets;
- NSLog(@"當前票數是:%d,售出:%d,線程名:%@",tickets,count,[[NSThread currentThread] name]);
- tickets--;
- }else{
- break;
- }
- [ticketsCondition unlock];
- }
- }
- - (void)dealloc {
- [ticketsThreadone release];
- [ticketsThreadtwo release];
- [ticketsCondition release];
- [window release];
- [super dealloc];
- }
- @end
三、線程的交互
線程在運行過程中,可能需要與其它線程進行通信,如在主線程中修改界面等等,可以使用如下接口:
- (void)performSelectorOnMainThread:
- (SEL)aSelector withObject:
- (id)arg waitUntilDone:
- (BOOL)wait
由於在本過程中,可能需要釋放一些資源,則需要使用NSAutoreleasePool來進行管理,如:
- (void)startTheBackgroundJob {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- // to do something in your thread job
- ...
- [self performSelectorOnMainThread:@selector(makeMyProgressBarMoving) withObject:nil waitUntilDone:NO];
- [pool release];
- }
舉例說明怎麼簡單的創建一個子線程。
用到的類是NSThread類,這裏使用detachNewTheadSelector:toTagaet:withObject創建一個線程。
函數setupThread:(NSArray*)userInfor。通過userInfor將需要的數據傳到線程中。
函數定義:
[代碼]c#/cpp/oc代碼:
01 | -( void )setupThread:(NSArray*)userInfor{ |
03 | [NSThread
detachNewThreadSelector:@selector(threadFunc:) toTarget:self withObject:(id)userInfor]; |
07 | -
( void )threadFunc:(id)userInfor{ |
09 | NSAutoreleasePool*pool
= [[NSAutoreleasePool alloc] init]; |
15 | [self
performSelectorOnMainThread:@selector(endThread) withObject:nil waitUntilDone:NO]; |
performSelectorOnMainThread通知主線程執行函數endThread。也可以使用performSelector:onThread:withObject:waitUntil 通知某線程執行線程結束後的處理。
線程內不要刷新界面。如果需要刷新界面,通過performSelectorOnMainThread,調出主線程中的方法去刷新。
例如,啓動一個線程下載圖片:
//啓動線程
[NSThread detachNewThreadSelector:@selector(downloadImage:) toTarget:self withObject:url];
//線程函數
[代碼]c#/cpp/oc代碼:
01 | -
( void )
downloadImage:(NSString*)url{ |
03 | _subThreed
= [NSThread currentThread]; |
05 | self.uploadPool
= [[NSAutoreleasePool alloc] init]; |
06 | self.characterBuffer
= [NSMutableData data]; |
08 | [[NSURLCache
sharedURLCache] removeAllCachedResponses]; |
10 | NSMutableURLRequest
*theRequest = [NSMutableURLRequest requestWithURL:[NSURLURLWithString:url]]; |
12 | self.connection
= [[NSURLConnection alloc] initWithRequest:theRequest delegate :self]; |
13 | [self
performSelectorOnMainThread:@selector(httpConnectStart) withObject:nil waitUntilDone:NO]; |
14 | if (connection
!= nil) { |
16 | [[NSRunLoop
currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; |
20 | self.photo
= [UIImage imageWithData:characterBuffer]; |
24 | [self
performSelectorOnMainThread:@selector(fillPhoto) withObject:nil waitUntilDone:NO]; |
26 | //
Release resources used only in this thread. |
27 | self.connection
= nil; |
29 | self.uploadPool
= nil; |
36 | #pragma
mark NSURLConnection Delegate methods |
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. |
41 | -
(NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { |
46 | //
Forward errors to the delegate. |
47 | -
( void )connection:(NSURLConnection
*)connection didFailWithError:(NSError *)error { |
49 | [self
performSelectorOnMainThread:@selector(httpConnectEnd) withObject:nil waitUntilDone:NO]; |
50 | [characterBuffer
setLength:0]; |
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. |
58 | [characterBuffer
appendData:data]; |
62 | -
( void )connectionDidFinishLoading:(NSURLConnection
*)connection { |
64 | [self
performSelectorOnMainThread:@selector(httpConnectEnd) withObject:nil waitUntilDone:NO]; |
65 | //
Set the condition which ends the run loop. |
首先我們需要創建一個線程有兩種方法:
- (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;
}
轉自:http://blog.csdn.net/chengyingzhilian/article/details/7885522