天天看點

Object-C學習筆錄(四)多線程程式設計

多線程的方式:

有java基礎的人都知道java的線程方式有繼承Thread類或者實作Runnable接口并覆寫run方法;而在Object-C裡面建立線程的方式有三種:NSThread建立線程 ;GCD建立線程;NSOperation建立線程

1.1NSThread方式

NSThread建立線程很簡單;咱看一段代碼就可以明白其原理:

-(void)userNSThread{

    NSThread *thread=[[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"nnnn"];

    [thread setName:@"subThread"];

    flag=YES;

    [thread start];

}

-(void)run:(NSString *)param{

    while(flag){

        [NSThread sleepForTimeInterval:1.0];

        NSLog(@"------%@------%@-----",[NSThread currentThread],param);   

    }

}

說明:NSThread有兩種初始化方式,一種就是上述代碼所示的[[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"nnnn"];其中run方法就是線程多執行體,就類似于java線程裡面的run方法,隻是Object—C的這個run方法的名稱是随便取的,還可以傳遞參數(就是最後的object參數,這裡我随便傳了一個@"nnnn"),不過隻能(至多隻能一個參數)傳遞一個object參數進去,當然對于面向對象的程式設計,如果要傳多個變量進去,我們可以把要傳的變量都封裝成一個類裡面,再将這個類的對象當作參數傳進去就OK了。還可以為線程設定名稱[thread setName:@"subThread"];。注意:這種初始化方式完成之後,還需要調用start方法才會啟動線程;這點和java點多線程相似。NSThread的另外一種初始化方式:

[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"nnnn"];這種方式建立的線程是匿名的線程,并且他初始化完成就直接啟動,而不是等待調用start方法,其他的執行體事run方法,傳遞參數等都和第一種方式一樣;

如何退出一個正在執行對子線程?如果線程的執行體裡面有循環,最通用多辦法就是設定一個flag信号,把flag加到循環體的判斷條件裡,到需要停止自線程的時候,隻需要把flag值修改一下,線程自動退出。比如:

-(void)run:(NSString *)param{

    while(flag){

        [NSThread sleepForTimeInterval:1.0];

        NSLog(@"------%@------%@-----",[NSThread currentThread],param);   

    }

}

要結束線程,隻要把flag的值修改即可;

也可以使用Object-C提供的類方法[NSThread exit];退出,比如在循環體這樣寫:

-(void)run:(NSString *)param{

    while(true){

        if([NSThread currentThread].isCancelled){

            [NSThread exit];//break;//此處調用break跳出循環體也可以結束線程

        }

        NSLog(@"------%@------",[NSThread currentThread]);

        [NSThread sleepForTimeInterval:1.0];//暫停一秒

    }

}

這樣寫還需要當想結束線程的時候調用[thread cancel];這樣[NSThread currentThread].isCancelled的傳回值就變成NO了,

2. NSOperation和NSOperationQueue方式

   首先NSOperation類似于java的Runnable接口;NSOperation有兩個子類NSInvocationOperation和NSBlockOperation;類似于Java的Runnable,NSOperation也允許開發者通過繼承實作來自定義子類來實作線程,這樣開發者隻需要重寫NSOperation的main方法即可。注意:聲明NSOperation 對象也有start方法,但是如果在主線程調用他的start方法;則執行體會被執行,但是是在主線程下執行,而不是建立一個線程;也就是他會在目前調用它start方法的線程裡執行;而不是建立新線程。如下代碼:

-(void)startOPerationThread{

    NSInvocationOperation *operation=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run1:) object:@"nnnn"];

    [operation start];

}

-(void)run1:(NSString *)n{

    NSLog(@"------%@------%@",[NSThread currentThread],n);

}

我們用NSLog列印目前所在的線程結果:

2014-12-27 11:09:18.414 networktest[1133:417103] ------<NSThread: 0x14e4a120>{number = 1, name = main}------nnnn

   要使用NSOperation建立新線程這裡還要用到NSOperationQueue,一看名字就知道他是一個隊列,它是一個FIFO(First In First Out 先進先出)隊列;它的底層是一個線程池,由于它是FIFO隊列;是以被送出到這個隊列點任務都是先送出到先開始被執行,注意這裡隻是先被執行,但不是先被執行完成,也就是說有可能有的線程任務比較耗時,是以,即使先被執行,也有可能最後完成的;這裡的意思就是NSOperationQueue先進先出與線程的并發與否無關,它隻保證最先送出對線程任務最先開始執行,而不保證每個線程任務執行完成的順序。 我們看看NSOperation與NSOperationQueue是如何結合使用的:

-(void)startOPerationThread{

NSOperationQueue *queue=[[NSOperationQueue alloc] init];

    queue.maxConcurrentOperationCount=3;//設定目前最大工作線程數目

    NSInvocationOperation *operation=[[NSInvocationOperation alloc] initWithTarget:selfselector:@selector(run1:) object:@"nnnn"];

    [queue addOperation:operation];//添加第一個任務

    NSBlockOperation *operation=[NSBlockOperation blockOperationWithBlock:^(void){

        NSLog(@"------%@------",[NSThread currentThread]);

    }];

    [queue addOperation:operation];//添加第二個任務

}

-(void)run1:(NSString *)n{

    NSLog(@"------%@------%@",[NSThread currentThread],n);

}

自定義NSOperation的子類:

@interface MyOperation : NSOperation

@end

@implementation MyOperation

-(void)main{

    NSLog(@"ddd-----%@",[NSThread currentThread]);

}

@end

使用自定義的NSOperation子類:

-(void)startMyOperation{

    MyOperation *operation=[[MyOperation alloc] init];

    [queue addOperation:operation];

}

   有趣的是我發現在java裡面這麼一段描述:這個線程池底層維護一個線程隊列;而在Object-C裡面是這樣描述:這個隊列底層維護一個線程池;嘿嘿,實作方面可能略有不同,但感覺其實都是線程池的功能;

3.GCD(Grand Central Dispatch)方式

關于GCD就不寫了,這裡引用一篇網友寫的文章吧

GCD介紹(一)