自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

關(guān)于iOS多線程,你看我就夠了

移動(dòng)開發(fā)
在這篇文章中,我將為你整理一下 iOS 開發(fā)中幾種多線程方案,以及其使用方法和注意事項(xiàng)。當(dāng)然也會(huì)給出幾種多線程的案例,在實(shí)際使用中感受它們的區(qū)別。還有一點(diǎn)需要說明的是,這篇文章將會(huì)使用 Swift 和 Objective-c 兩種語言講解,雙語幼兒園。OK,let't begin!

[[142590]]

在這篇文章中,我將為你整理一下 iOS 開發(fā)中幾種多線程方案,以及其使用方法和注意事項(xiàng)。當(dāng)然也會(huì)給出幾種多線程的案例,在實(shí)際使用中感受它們的區(qū)別。還有一點(diǎn)需要說明的是,這篇文章將會(huì)使用 Swift 和 Objective-c 兩種語言講解,雙語幼兒園。OK,let't begin!

概述

這篇文章中,我不會(huì)說多線程是什么、線程和進(jìn)程的區(qū)別、多線程有什么用,當(dāng)然我也不會(huì)說什么是串行、什么是并行等問題,這些我們應(yīng)該都知道的。

在 iOS 中其實(shí)目前有 4 套多線程方案,他們分別是:

  • Pthreads
  • NSThread
  • GCD
  • NSOperation & NSOperationQueue

所以接下來,我會(huì)一一講解這些方案的使用方法和一些案例。在將這些內(nèi)容的時(shí)候,我也會(huì)順帶說一些多線程周邊產(chǎn)品。比如: 線程同步、 延時(shí)執(zhí)行、 單例模式 等等。

Pthreads

其實(shí)這個(gè)方案不用說的,只是拿來充個(gè)數(shù),為了讓大家了解一下就好了。百度百科里是這么說的:

POSIX線程(POSIX threads),簡(jiǎn)稱Pthreads,是線程的POSIX標(biāo)準(zhǔn)。該標(biāo)準(zhǔn)定義了創(chuàng)建和操縱線程的一整套API。在類Unix操作系統(tǒng)(Unix、Linux、Mac OS X等)中,都使用Pthreads作為操作系統(tǒng)的線程。

簡(jiǎn)單地說,這是一套在很多操作系統(tǒng)上都通用的多線程API,所以移植性很強(qiáng)(然并卵),當(dāng)然在 iOS 中也是可以的。不過這是基于 c語言 的框架,使用起來這酸爽!感受一下:

OBJECTIVE-C

當(dāng)然第一步要包含頭文件

#import <pthread.h>

然后創(chuàng)建線程,并執(zhí)行任務(wù)

  1. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
  2.     pthread_t thread; 
  3.     //創(chuàng)建一個(gè)線程并自動(dòng)執(zhí)行 
  4.     pthread_create(&thread, NULL, start, NULL); 
  5.  
  6. void *start(void *data) { 
  7.     NSLog(@"%@", [NSThread currentThread]); 
  8.  
  9.     return NULL; 

打印輸出:

2015-07-27 23:57:21.689 testThread[10616:2644653] <NSThread: 0x7fbb48d33690>{number = 2, name = (null)}

看代碼就會(huì)發(fā)現(xiàn)他需要 c語言函數(shù),這是比較蛋疼的,更蛋疼的是你需要手動(dòng)處理線程的各個(gè)狀態(tài)的轉(zhuǎn)換即管理生命周期,比如,這段代碼雖然創(chuàng)建了一個(gè)線程,但并沒有銷毀。

SWIFT

很遺憾,在我目前的 swift1.2 中無法執(zhí)行這套方法,原因是這個(gè)函數(shù)需要傳入一個(gè)函數(shù)指針 CFunctionPointer<T> 類型,但是目前 swift 無法將方法轉(zhuǎn)換成此類型。聽說 swift 2.0 引入一個(gè)新特性 @convention(c), 可以完成 Swift 方法轉(zhuǎn)換成 c 語言指針的。在這里可以看到

那么,Pthreads 方案的多線程我就介紹這么多,畢竟做 iOS 開發(fā)幾乎不可能用到。但是如果你感興趣的話,或者說想要自己實(shí)現(xiàn)一套多線程方案,從底層開始定制,那么可以去搜一下相關(guān)資料。

NSThread

這套方案是經(jīng)過蘋果封裝后的,并且完全面向?qū)ο蟮?。所以你可以直接操控線程對(duì)象,非常直觀和方便。但是,它的生命周期還是需要我們手動(dòng)管理,所以這套方案也是偶爾用用,比如 [NSThread currentThread],它可以獲取當(dāng)前線程類,你就可以知道當(dāng)前線程的各種屬性,用于調(diào)試十分方便。下面來看看它的一些用法。

創(chuàng)建并啟動(dòng)

  • 先創(chuàng)建線程類,再啟動(dòng)

    OBJECTIVE-C

    1. // 創(chuàng)建 
    2.   NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil]; 
    3.  
    4.   // 啟動(dòng) 
    5.   [thread start]; 

    SWIFT

    1. //創(chuàng)建 
    2.   let thread = NSThread(target: self, selector: "run:", object: nil) 
    3.  
    4.   //啟動(dòng) 
    5.   thread.start() 
  • 創(chuàng)建并自動(dòng)啟動(dòng)

    OBJECTIVE-C

    1. [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil]; 

    SWIFT

    1. NSThread.detachNewThreadSelector("run:", toTarget: self, withObject: nil) 
  • 使用 NSObject 的方法創(chuàng)建并自動(dòng)啟動(dòng)

    OBJECTIVE-C

    1. [self performSelectorInBackground:@selector(run:) withObject:nil]; 

    SWIFT

    很遺憾 too! 蘋果認(rèn)為 performSelector: 不安全,所以在 Swift 去掉了這個(gè)方法。

    Note: The performSelector: method and related selector-invoking methods are not imported in Swift because they are inherently unsafe.

其他方法

除了創(chuàng)建啟動(dòng)外,NSThread 還以很多方法,下面我列舉一些常見的方法,當(dāng)然我列舉的并不完整,更多方法大家可以去類的定義里去看。

OBJECTIVE-C

  1. //取消線程 
  2. - (void)cancel; 
  3.  
  4. //啟動(dòng)線程 
  5. - (void)start; 
  6.  
  7. //判斷某個(gè)線程的狀態(tài)的屬性 
  8. @property (readonly, getter=isExecuting) BOOL executing; 
  9. @property (readonly, getter=isFinished) BOOL finished; 
  10. @property (readonly, getter=isCancelled) BOOL cancelled; 
  11.  
  12. //設(shè)置和獲取線程名字 
  13. -(void)setName:(NSString *)n; 
  14. -(NSString *)name; 
  15.  
  16. //獲取當(dāng)前線程信息 
  17. + (NSThread *)currentThread; 
  18.  
  19. //獲取主線程信息 
  20. + (NSThread *)mainThread; 
  21.  
  22. //使當(dāng)前線程暫停一段時(shí)間,或者暫停到某個(gè)時(shí)刻 
  23. + (void)sleepForTimeInterval:(NSTimeInterval)time; 
  24. + (void)sleepUntilDate:(NSDate *)date; 

SWIFT

Swift的方法名字和OC的方法名都一樣,我就不浪費(fèi)空間列舉出來了。

其實(shí),NSThread 用起來也挺簡(jiǎn)單的,因?yàn)樗湍菐追N方法。同時(shí),我們也只有在一些非常簡(jiǎn)單的場(chǎng)景才會(huì)用 NSThread, 畢竟它還不夠智能,不能優(yōu)雅地處理多線程中的其他高級(jí)概念。所以接下來要說的內(nèi)容才是重點(diǎn)。

GCD

Grand Central Dispatch,聽名字就霸氣。它是蘋果為多核的并行運(yùn)算提出的解決方案,所以會(huì)自動(dòng)合理地利用更多的CPU內(nèi)核(比如雙核、四核),最重要的是它會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程),完全不需要我們管理,我們只需要告訴干什么就行。同時(shí)它使用的也是 c語言,不過由于使用了 Block(Swift里叫做閉包),使得使用起來更加方便,而且靈活。所以基本上大家都使用 GCD 這套方案,老少咸宜,實(shí)在是居家旅行、殺人滅口,必備良藥。不好意思,有點(diǎn)中二,咱們繼續(xù)。

任務(wù)和隊(duì)列

在 GCD 中,加入了兩個(gè)非常重要的概念: 任務(wù) 和 隊(duì)列。

  • 任務(wù):即操作,你想要干什么,說白了就是一段代碼,在 GCD 中就是一個(gè) Block,所以添加任務(wù)十分方便。任務(wù)有兩種執(zhí)行方式: 同步執(zhí)行 和 異步執(zhí)行,他們之間的區(qū)別是 是否會(huì)創(chuàng)建新的線程。

    同步執(zhí)行:只要是同步執(zhí)行的任務(wù),都會(huì)在當(dāng)前線程執(zhí)行,不會(huì)另開線程。

    異步執(zhí)行:只要是異步執(zhí)行的任務(wù),都會(huì)另開線程,在別的線程執(zhí)行。

  • 隊(duì)列:用于存放任務(wù)。一共有兩種隊(duì)列, 串行隊(duì)列 和 并行隊(duì)列。

    串行隊(duì)列 中的任務(wù)會(huì)根據(jù)隊(duì)列的定義 FIFO 的執(zhí)行,一個(gè)接一個(gè)的先進(jìn)先出的進(jìn)行執(zhí)行。

    并行隊(duì)列 中的任務(wù)根據(jù)同步或異步有不同的執(zhí)行方式。雖然很繞,但請(qǐng)看下表:

  同步執(zhí)行 異步執(zhí)行
串行隊(duì)列 當(dāng)前線程,一個(gè)一個(gè)執(zhí)行          其他線程,一個(gè)一個(gè)執(zhí)行
并行隊(duì)列 當(dāng)前線程,一個(gè)一個(gè)執(zhí)行          開很多線程,一起執(zhí)行

創(chuàng)建隊(duì)列

  • 主隊(duì)列:這是一個(gè)特殊的 串行隊(duì)列。什么是主隊(duì)列,大家都知道吧,它用于刷新 UI,任何需要刷新 UI 的工作都要在主隊(duì)列執(zhí)行,所以一般耗時(shí)的任務(wù)都要放到別的線程執(zhí)行。

    1. //OBJECTIVE-C 
    2.   dispatch_queue_t queue = ispatch_get_main_queue(); 
    3.  
    4.   //SWIFT 
    5.   let queue = ispatch_get_main_queue() 
  • 自己創(chuàng)建的隊(duì)列:凡是自己創(chuàng)建的隊(duì)列都是 串行隊(duì)列。其中第一個(gè)參數(shù)是標(biāo)識(shí)符,用于 DEBUG 的時(shí)候標(biāo)識(shí)唯一的隊(duì)列,可以為空。大家可以看xcode的文檔查看參數(shù)意義。

    1. //OBJECTIVE-C 
    2.   dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL); 
    3.  
    4.   //SWIFT 
    5.   let queue = dispatch_queue_create("tk.bourne.testQueue", nil); 
  • 全局并行隊(duì)列:這應(yīng)該是唯一一個(gè)并行隊(duì)列,只要是并行任務(wù)一般都加入到這個(gè)隊(duì)列。

    1. //OBJECTIVE-C 
    2.   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    3.  
    4.   //SWIFT 
    5.   let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0

創(chuàng)建任務(wù)

  • 同步任務(wù):不會(huì)另開線程 (SYNC)

    OBJECTIVE-C

    1. dispatch_sync(<#queue#>, ^{ 
    2.       //code here 
    3.       NSLog(@"%@", [NSThread currentThread]); 
    4.   }); 

    SWIFT

    1. dispatch_sync(<#queue#>, { () -> Void in 
    2.       //code here 
    3.       println(NSThread.currentThread()) 
    4.   }) 
  • 異步任務(wù):會(huì)另開線程 (ASYNC)

    OBJECTIVE-C

    1. dispatch_async(<#queue#>, ^{ 
    2.       //code here 
    3.       NSLog(@"%@", [NSThread currentThread]); 
    4.   }); 

    SWIFT

    1. dispatch_async(<#queue#>, { () -> Void in 
    2.       //code here 
    3.       println(NSThread.currentThread()) 
    4.   }) 

隊(duì)列組

隊(duì)列組可以將很多隊(duì)列添加到一個(gè)組里,這樣做的好處是,當(dāng)這個(gè)組里所有的任務(wù)都執(zhí)行完了,隊(duì)列組會(huì)通過一個(gè)方法通知我們。下面是使用方法,這是一個(gè)很實(shí)用的功能。

OBJECTIVE-C

  1. //1.創(chuàng)建隊(duì)列組 
  2. dispatch_group_t group = dispatch_group_create(); 
  3. //2.創(chuàng)建隊(duì)列 
  4. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
  5.  
  6. //3.多次使用隊(duì)列組的方法執(zhí)行任務(wù), 只有異步方法 
  7. //3.1.執(zhí)行3次循環(huán) 
  8. dispatch_group_async(group, queue, ^{ 
  9.     for (NSInteger i = 0; i < 3; i++) { 
  10.         NSLog(@"group-01 - %@", [NSThread currentThread]); 
  11.     } 
  12. }); 
  13.  
  14. //3.2.主隊(duì)列執(zhí)行8次循環(huán) 
  15. dispatch_group_async(group, dispatch_get_main_queue(), ^{ 
  16.     for (NSInteger i = 0; i < 8; i++) { 
  17.         NSLog(@"group-02 - %@", [NSThread currentThread]); 
  18.     } 
  19. }); 
  20.  
  21. //3.3.執(zhí)行5次循環(huán) 
  22. dispatch_group_async(group, queue, ^{ 
  23.     for (NSInteger i = 0; i < 5; i++) { 
  24.         NSLog(@"group-03 - %@", [NSThread currentThread]); 
  25.     } 
  26. }); 
  27.  
  28. //4.都完成后會(huì)自動(dòng)通知 
  29. dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
  30.     NSLog(@"完成 - %@", [NSThread currentThread]); 
  31. }); 

SWIFT

  1. //1.創(chuàng)建隊(duì)列組 
  2. let group = dispatch_group_create() 
  3. //2.創(chuàng)建隊(duì)列 
  4. let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0
  5.  
  6. //3.多次使用隊(duì)列組的方法執(zhí)行任務(wù), 只有異步方法 
  7. //3.1.執(zhí)行3次循環(huán) 
  8. dispatch_group_async(group, queue) { () -> Void in 
  9.     for _ in 0..<3 { 
  10.         NSLog("group-01 - %@", NSThread.currentThread()) 
  11.     } 
  12.  
  13. //3.2.主隊(duì)列執(zhí)行8次循環(huán) 
  14. dispatch_group_async(group, dispatch_get_main_queue()) { () -> Void in 
  15.     for _ in 0..<8 { 
  16.         NSLog("group-02 - %@", NSThread.currentThread()) 
  17.     } 
  18.  
  19. //3.3.執(zhí)行5次循環(huán) 
  20. dispatch_group_async(group, queue) { () -> Void in 
  21.     for _ in 0..<5 { 
  22.         NSLog("group-03 - %@", NSThread.currentThread()) 
  23.     } 
  24.  
  25. //4.都完成后會(huì)自動(dòng)通知 
  26. dispatch_group_notify(group, dispatch_get_main_queue()) { () -> Void in 
  27.     NSLog("完成 - %@", NSThread.currentThread()) 

打印結(jié)果

2015-07-28 03:40:34.277 test[12540:3319271] group-03 - <NSThread: 0x7f9772536f00>{number = 3, name = (null)}

2015-07-28 03:40:34.277 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}

2015-07-28 03:40:34.277 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}

2015-07-28 03:40:34.277 test[12540:3319271] group-03 - <NSThread: 0x7f9772536f00>{number = 3, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}

2015-07-28 03:40:34.278 test[12540:3319271] group-03 - <NSThread: 0x7f9772536f00>{number = 3, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319271] group-03 - <NSThread: 0x7f9772536f00>{number = 3, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}

2015-07-28 03:40:34.277 test[12540:3319273] group-01 - <NSThread: 0x7f977272e8d0>{number = 2, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319271] group-03 - <NSThread: 0x7f9772536f00>{number = 3, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}

2015-07-28 03:40:34.278 test[12540:3319273] group-01 - <NSThread: 0x7f977272e8d0>{number = 2, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}

2015-07-28 03:40:34.278 test[12540:3319273] group-01 - <NSThread: 0x7f977272e8d0>{number = 2, name = (null)}

2015-07-28 03:40:34.279 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}

2015-07-28 03:40:34.279 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}

2015-07-28 03:40:34.279 test[12540:3319146] 完成 - <NSThread: 0x7f977240ba60>{number = 1, name = main}

這些就是 GCD 的基本功能,但是它的能力遠(yuǎn)不止這些,等講完 NSOperation 后,我們?cè)賮砜纯此囊恍┢渌矫嬗猛?。而且,只要你想象力夠豐富,你可以組合出更好的用法。

更新:關(guān)于GCD,我講的時(shí)候不是很到位,可能忽略了一些東西,請(qǐng)看評(píng)論區(qū) 第一條。

NSOperation和NSOperationQueue

NSOperation 是蘋果公司對(duì) GCD 的封裝,完全面向?qū)ο?,所以使用起來更好理解?大家可以看到 NSOperation 和 NSOperationQueue 分別對(duì)應(yīng) GCD 的 任務(wù) 和 隊(duì)列 。操作步驟也很好理解:

  1. 將要執(zhí)行的任務(wù)封裝到一個(gè) NSOperation 對(duì)象中。
  2. 將此任務(wù)添加到一個(gè) NSOperationQueue 對(duì)象中。

然后系統(tǒng)就會(huì)自動(dòng)在執(zhí)行任務(wù)。至于同步還是異步、串行還是并行請(qǐng)繼續(xù)往下看:

添加任務(wù)

值得說明的是,NSOperation 只是一個(gè)抽象類,所以不能封裝任務(wù)。但它有 2 個(gè)子類用于封裝任務(wù)。分別是:NSInvocationOperation 和 NSBlockOperation 。創(chuàng)建一個(gè) Operation 后,需要調(diào)用 start 方法來啟動(dòng)任務(wù),它會(huì) 默認(rèn)在當(dāng)前隊(duì)列同步執(zhí)行。當(dāng)然你也可以在中途取消一個(gè)任務(wù),只需要調(diào)用其 cancel 方法即可。

  • NSInvocationOperation : 需要傳入一個(gè)方法名。

    OBJECTIVE-C

    1. //1.創(chuàng)建NSInvocationOperation對(duì)象 
    2.   NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil]; 
    3.  
    4.   //2.開始執(zhí)行 
    5.   [operation start]; 

    SWIFT

    在 Swift 構(gòu)建的和諧社會(huì)里,是容不下 NSInvocationOperation 這種不是類型安全的敗類的。蘋果如是說。這里有相關(guān)解釋

  • NSBlockOperation

    OBJECTIVE-C

    1. //1.創(chuàng)建NSBlockOperation對(duì)象 
    2.   NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ 
    3.       NSLog(@"%@", [NSThread currentThread]); 
    4.   }]; 
    5.  
    6.   //2.開始任務(wù) 
    7.   [operation start]; 

    SWIFT

    1. //1.創(chuàng)建NSBlockOperation對(duì)象 
    2.   let operation = NSBlockOperation { () -> Void in 
    3.       println(NSThread.currentThread()) 
    4.   } 
    5.  
    6.   //2.開始任務(wù) 
    7.   operation.start() 

    之前說過這樣的任務(wù),默認(rèn)會(huì)在當(dāng)前線程執(zhí)行。但是 NSBlockOperation 還有一個(gè)方法:addExecutionBlock: ,通過這個(gè)方法可以給 Operation 添加多個(gè)執(zhí)行 Block。這樣 Operation 中的任務(wù) 會(huì)并發(fā)執(zhí)行,它會(huì) 在主線程和其它的多個(gè)線程 執(zhí)行這些任務(wù),注意下面的打印結(jié)果:

    OBJECTIVE-C

    1. //1.創(chuàng)建NSBlockOperation對(duì)象 
    2.       NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ 
    3.           NSLog(@"%@", [NSThread currentThread]); 
    4.       }]; 
    5.  
    6.       //添加多個(gè)Block 
    7.       for (NSInteger i = 0; i < 5; i++) { 
    8.           [operation addExecutionBlock:^{ 
    9.               NSLog(@"第%ld次:%@", i, [NSThread currentThread]); 
    10.           }]; 
    11.       } 
    12.  
    13.       //2.開始任務(wù) 
    14.       [operation start]; 

    SWIFT

    1. //1.創(chuàng)建NSBlockOperation對(duì)象 
    2.         let operation = NSBlockOperation { () -> Void in 
    3.             NSLog("%@", NSThread.currentThread()) 
    4.         } 
    5.  
    6.         //2.添加多個(gè)Block 
    7.         for i in 0..<5 { 
    8.             operation.addExecutionBlock { () -> Void in 
    9.                 NSLog("第%ld次 - %@", i, NSThread.currentThread()) 
    10.             } 
    11.         } 
    12.  
    13.         //2.開始任務(wù) 
    14.         operation.start() 

    打印輸出

    2015-07-28 17:50:16.585 test[17527:4095467] 第2次 - <NSThread: 0x7ff5c9701910>{number = 1, name = main}

    2015-07-28 17:50:16.585 test[17527:4095666] 第1次 - <NSThread: 0x7ff5c972caf0>{number = 4, name = (null)}

    2015-07-28 17:50:16.585 test[17527:4095665] <NSThread: 0x7ff5c961b610>{number = 3, name = (null)}

    2015-07-28 17:50:16.585 test[17527:4095662] 第0次 - <NSThread: 0x7ff5c948d310>{number = 2, name = (null)}

    2015-07-28 17:50:16.586 test[17527:4095666] 第3次 - <NSThread: 0x7ff5c972caf0>{number = 4, name = (null)}

    2015-07-28 17:50:16.586 test[17527:4095467] 第4次 - <NSThread: 0x7ff5c9701910>{number = 1, name = main}

    NOTE:addExecutionBlock 方法必須在 start() 方法之前執(zhí)行,否則就會(huì)報(bào)錯(cuò):

    ‘*** -[NSBlockOperation addExecutionBlock:]: blocks cannot be added after the operation has started executing or finished'

    NOTE:大家可能發(fā)現(xiàn)了一個(gè)問題,為什么我在 Swift 里打印輸出使用 NSLog() 而不是 println() 呢?原因是使用 print() / println() 輸出的話,它會(huì)簡(jiǎn)單地使用 流(stream) 的概念,學(xué)過 C++ 的都知道。它會(huì)把需要輸出的每個(gè)字符一個(gè)一個(gè)的輸出到控制臺(tái)。普通使用并沒有問題,可是當(dāng)多線程同步輸出的時(shí)候問題就來了,由于很多 println() 同時(shí)打印,就會(huì)導(dǎo)致控制臺(tái)上的字符混亂的堆在一起,而NSLog() 就沒有這個(gè)問題。到底是什么樣子的呢?你可以把上面 NSLog() 改為 println() ,然后一試便知。 更多 NSLog() 與 println() 的區(qū)別看這里

  • 自定義Operation

    除了上面的兩種 Operation 以外,我們還可以自定義 Operation。自定義 Operation 需要繼承 NSOperation 類,并實(shí)現(xiàn)其 main() 方法,因?yàn)樵谡{(diào)用 start()方法的時(shí)候,內(nèi)部會(huì)調(diào)用 main() 方法完成相關(guān)邏輯。所以如果以上的兩個(gè)類無法滿足你的欲望的時(shí)候,你就需要自定義了。你想要實(shí)現(xiàn)什么功能都可以寫在里面。除此之外,你還需要實(shí)現(xiàn) cancel() 在內(nèi)的各種方法。所以這個(gè)功能提供給高級(jí)玩家,我在這里就不說了,等我需要用到時(shí)在研究它,到時(shí)候可能會(huì)再做更新。

創(chuàng)建隊(duì)列

看過上面的內(nèi)容就知道,我們可以調(diào)用一個(gè) NSOperation 對(duì)象的 start() 方法來啟動(dòng)這個(gè)任務(wù),但是這樣做他們默認(rèn)是 同步執(zhí)行 的。就算是 addExecutionBlock 方法,也會(huì)在當(dāng)前線程和其他線程 中執(zhí)行,也就是說還是會(huì)占用當(dāng)前線程。這是就要用到隊(duì)列 NSOperationQueue 了。而且,按類型來說的話一共有兩種類型:主隊(duì)列、其他隊(duì)列。只要添加到隊(duì)列,會(huì)自動(dòng)調(diào)用任務(wù)的 start() 方法

  • 主隊(duì)列

    細(xì)心的同學(xué)就會(huì)發(fā)現(xiàn),每套多線程方案都會(huì)有一個(gè)主線程(當(dāng)然啦,說的是iOS中,像 pthread 這種多系統(tǒng)的方案并沒有,因?yàn)?nbsp;UI線程 理論需要每種操作系統(tǒng)自己定制)。這是一個(gè)特殊的線程,必須串行。所以添加到主隊(duì)列的任務(wù)都會(huì)一個(gè)接一個(gè)地排著隊(duì)在主線程處理。

    1. //OBJECTIVE-C 
    2. NSOperationQueue *queue = [NSOperationQueue mainQueue]; 
    3.  
    4. //SWIFT 
    5. let queue = NSOperationQueue.mainQueue() 
  • 其他隊(duì)列

    因?yàn)橹麝?duì)列比較特殊,所以會(huì)單獨(dú)有一個(gè)類方法來獲得主隊(duì)列。那么通過初始化產(chǎn)生的隊(duì)列就是其他隊(duì)列了,因?yàn)橹挥羞@兩種隊(duì)列,除了主隊(duì)列,其他隊(duì)列就不需要名字了。

    注意:其他隊(duì)列的任務(wù)會(huì)在其他線程并行執(zhí)行。

    OBJECTIVE-C

    1. //1.創(chuàng)建一個(gè)其他隊(duì)列     
    2. NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
    3.  
    4. //2.創(chuàng)建NSBlockOperation對(duì)象 
    5. NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ 
    6.     NSLog(@"%@", [NSThread currentThread]); 
    7. }]; 
    8.  
    9. //3.添加多個(gè)Block 
    10. for (NSInteger i = 0; i < 5; i++) { 
    11.     [operation addExecutionBlock:^{ 
    12.         NSLog(@"第%ld次:%@", i, [NSThread currentThread]); 
    13.     }]; 
    14.  
    15. //4.隊(duì)列添加任務(wù) 
    16. [queue addOperation:operation]; 

    SWIFT

    1. //1.創(chuàng)建其他隊(duì)列 
    2. let queue = NSOperationQueue() 
    3.  
    4. //2.創(chuàng)建NSBlockOperation對(duì)象 
    5. let operation = NSBlockOperation { () -> Void in 
    6.     NSLog("%@", NSThread.currentThread()) 
    7.  
    8. //3.添加多個(gè)Block 
    9. for i in 0..<5 { 
    10.     operation.addExecutionBlock { () -> Void in 
    11.         NSLog("第%ld次 - %@", i, NSThread.currentThread()) 
    12.     } 
    13.  
    14. //4.隊(duì)列添加任務(wù) 
    15. queue.addOperation(operation) 

    打印輸出

    2015-07-28 20:26:28.463 test[18622:4443534] <NSThread: 0x7fd022c3ac10>{number = 5, name = (null)}

    2015-07-28 20:26:28.463 test[18622:4443536] 第2次 - <NSThread: 0x7fd022e36d50>{number = 2, name = (null)}

    2015-07-28 20:26:28.463 test[18622:4443535] 第0次 - <NSThread: 0x7fd022f237f0>{number = 4, name = (null)}

    2015-07-28 20:26:28.463 test[18622:4443533] 第1次 - <NSThread: 0x7fd022d372b0>{number = 3, name = (null)}

    2015-07-28 20:26:28.463 test[18622:4443534] 第3次 - <NSThread: 0x7fd022c3ac10>{number = 5, name = (null)}

    2015-07-28 20:26:28.463 test[18622:4443536] 第4次 - <NSThread: 0x7fd022e36d50>{number = 2, name = (null)}

OK, 這時(shí)應(yīng)該發(fā)問了,大家將 NSOperationQueue 與 GCD的隊(duì)列 相比較就會(huì)發(fā)現(xiàn),這里沒有并行隊(duì)列,那如果我想要10個(gè)任務(wù)在其他線程串行的執(zhí)行怎么辦?

這就是蘋果封裝的妙處,你不用管串行、并行、同步、異步這些名詞。NSOperationQueue有一個(gè)參數(shù) maxConcurrentOperationCount 最大并發(fā)數(shù),用來設(shè)置最多可以讓多少個(gè)任務(wù)同時(shí)執(zhí)行。當(dāng)你把它設(shè)置為 1 的時(shí)候,他不就是串行了嘛!

NSOperationQueue 還有一個(gè)添加任務(wù)的方法,- (void)addOperationWithBlock:(void (^)(void))block; ,這是不是和 GCD 差不多?這樣就可以添加一個(gè)任務(wù)到隊(duì)列中了,十分方便。

NSOperation 有一個(gè)非常實(shí)用的功能,那就是添加依賴。比如有 3 個(gè)任務(wù):A: 從服務(wù)器上下載一張圖片,B:給這張圖片加個(gè)水印,C:把圖片返回給服務(wù)器。這時(shí)就可以用到依賴了:

OBJECTIVE-C

  1. //1.任務(wù)一:下載圖片 
  2. NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ 
  3.     NSLog(@"下載圖片 - %@", [NSThread currentThread]); 
  4.     [NSThread sleepForTimeInterval:1.0]; 
  5. }]; 
  6.  
  7. //2.任務(wù)二:打水印 
  8. NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ 
  9.     NSLog(@"打水印   - %@", [NSThread currentThread]); 
  10.     [NSThread sleepForTimeInterval:1.0]; 
  11. }]; 
  12.  
  13. //3.任務(wù)三:上傳圖片 
  14. NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{ 
  15.     NSLog(@"上傳圖片 - %@", [NSThread currentThread]); 
  16.     [NSThread sleepForTimeInterval:1.0]; 
  17. }]; 
  18.  
  19. //4.設(shè)置依賴 
  20. [operation2 addDependency:operation1];      //任務(wù)二依賴任務(wù)一 
  21. [operation3 addDependency:operation2];      //任務(wù)三依賴任務(wù)二 
  22.  
  23. //5.創(chuàng)建隊(duì)列并加入任務(wù) 
  24. NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
  25. [queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO]; 

SWIFT

  1. //1.任務(wù)一:下載圖片 
  2. let operation1 = NSBlockOperation { () -> Void in 
  3.     NSLog("下載圖片 - %@", NSThread.currentThread()) 
  4.     NSThread.sleepForTimeInterval(1.0
  5.  
  6. //2.任務(wù)二:打水印 
  7. let operation2 = NSBlockOperation { () -> Void in 
  8.     NSLog("打水印   - %@", NSThread.currentThread()) 
  9.     NSThread.sleepForTimeInterval(1.0
  10.  
  11. //3.任務(wù)三:上傳圖片 
  12. let operation3 = NSBlockOperation { () -> Void in 
  13.     NSLog("上傳圖片 - %@", NSThread.currentThread()) 
  14.     NSThread.sleepForTimeInterval(1.0
  15.  
  16. //4.設(shè)置依賴 
  17. operation2.addDependency(operation1)    //任務(wù)二依賴任務(wù)一 
  18. operation3.addDependency(operation2)    //任務(wù)三依賴任務(wù)二 
  19.  
  20. //5.創(chuàng)建隊(duì)列并加入任務(wù) 
  21. let queue = NSOperationQueue() 
  22. queue.addOperations([operation3, operation2, operation1], waitUntilFinished: false

打印結(jié)果

2015-07-28 21:24:28.622 test[19392:4637517] 下載圖片 - <NSThread: 0x7fc10ad4d970>{number = 2, name = (null)}

2015-07-28 21:24:29.622 test[19392:4637515] 打水印 - <NSThread: 0x7fc10af20ef0>{number = 3, name = (null)}

2015-07-28 21:24:30.627 test[19392:4637515] 上傳圖片 - <NSThread: 0x7fc10af20ef0>{number = 3, name = (null)}

  • 注意:不能添加相互依賴,會(huì)死鎖,比如 A依賴B,B依賴A。
  • 可以使用 removeDependency 來解除依賴關(guān)系。
  • 可以在不同的隊(duì)列之間依賴,反正就是這個(gè)依賴是添加到任務(wù)身上的,和隊(duì)列沒關(guān)系。

其他方法

以上就是一些主要方法, 下面還有一些常用方法需要大家注意:

  • NSOperation

    BOOL executing; //判斷任務(wù)是否正在執(zhí)行

    BOOL finished; //判斷任務(wù)是否完成

    void (^completionBlock)(void); //用來設(shè)置完成后需要執(zhí)行的操作

    - (void)cancel; //取消任務(wù)

    - (void)waitUntilFinished; //阻塞當(dāng)前線程直到此任務(wù)執(zhí)行完畢

  • NSOperationQueue

    NSUInteger operationCount; //獲取隊(duì)列的任務(wù)數(shù)

    - (void)cancelAllOperations; //取消隊(duì)列中所有的任務(wù)

    - (void)waitUntilAllOperationsAreFinished; //阻塞當(dāng)前線程直到此隊(duì)列中的所有任務(wù)執(zhí)行完畢

    [queue setSuspended:YES]; // 暫停queue

    [queue setSuspended:NO]; // 繼續(xù)queue

好啦,到這里差不多就講完了。當(dāng)然,我講的并不完整,可能有一些知識(shí)我并沒有講到,但作為常用方法,這些已經(jīng)足夠了。不過我在這里只是告訴你了一些方法的功能,只是怎么把他們用到合適的地方,就需要多多實(shí)踐了。下面我會(huì)說一些關(guān)于多線程的案例,是大家更加什么地了解。

其他用法

在這部分,我會(huì)說一些和多線程知識(shí)相關(guān)的案例,可能有些很簡(jiǎn)單,大家早都知道的,不過因?yàn)檫@篇文章講的是多線程嘛,所以應(yīng)該盡可能的全面嘛。還有就是,我會(huì)盡可能的使用多種方法實(shí)現(xiàn),讓大家看看其中的區(qū)別。

線程同步

所謂線程同步就是為了防止多個(gè)線程搶奪同一個(gè)資源造成的數(shù)據(jù)安全問題,所采取的一種措施。當(dāng)然也有很多實(shí)現(xiàn)方法,請(qǐng)往下看:

  • 互斥鎖 :給需要同步的代碼塊加一個(gè)互斥鎖,就可以保證每次只有一個(gè)線程訪問此代碼塊。

    OBJECTIVE-C

    1. @synchronized(self) { 
    2.     //需要執(zhí)行的代碼塊 

    SWIFT

    1. objc_sync_enter(self) 
    2. //需要執(zhí)行的代碼塊 
    3. objc_sync_exit(self) 
  • 同步執(zhí)行 :我們可以使用多線程的知識(shí),把多個(gè)線程都要執(zhí)行此段代碼添加到同一個(gè)串行隊(duì)列,這樣就實(shí)現(xiàn)了線程同步的概念。當(dāng)然這里可以使用 GCD 和 NSOperation兩種方案,我都寫出來。

    OBJECTIVE-C

  
  1. //GCD 
  2.   //需要一個(gè)全局變量queue,要讓所有線程的這個(gè)操作都加到一個(gè)queue中 
  3.   dispatch_sync(queue, ^{ 
  4.       NSInteger ticket = lastTicket; 
  5.       [NSThread sleepForTimeInterval:0.1]; 
  6.       NSLog(@"%ld - %@",ticket, [NSThread currentThread]); 
  7.       ticket -= 1
  8.       lastTicket = ticket; 
  9.   }); 
  10.  
  11.  
  12.   //NSOperation & NSOperationQueue 
  13.   //重點(diǎn):1. 全局的 NSOperationQueue, 所有的操作添加到同一個(gè)queue中 
  14.   //       2. 設(shè)置 queue 的 maxConcurrentOperationCount 為 1 
  15.   //       3. 如果后續(xù)操作需要Block中的結(jié)果,就需要調(diào)用每個(gè)操作的waitUntilFinished,阻塞當(dāng)前線程,一直等到當(dāng)前操作完成,才允許執(zhí)行后面的。waitUntilFinished 要在添加到隊(duì)列之后! 
  16.  
  17.   NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ 
  18.       NSInteger ticket = lastTicket; 
  19.       [NSThread sleepForTimeInterval:1]; 
  20.       NSLog(@"%ld - %@",ticket, [NSThread currentThread]); 
  21.       ticket -= 1
  22.       lastTicket = ticket; 
  23.   }]; 
  24.  
  25.   [queue addOperation:operation]; 
  26.  
  27.   [operation waitUntilFinished]; 
  28.  
  29.   //后續(xù)要做的事 

SWIFT

這里的 swift 代碼,我就不寫了,因?yàn)槊烤涠家粯?,只是語法不同而已,照著 OC 的代碼就能寫出 Swift 的。這篇文章已經(jīng)老長(zhǎng)老長(zhǎng)了,我就不浪費(fèi)篇幅了,又不是高中寫作文。

延遲執(zhí)行

所謂延遲執(zhí)行就是延時(shí)一段時(shí)間再執(zhí)行某段代碼。下面說一些常用方法。

  • perform

    OBJECTIVE-C

    1. // 3秒后自動(dòng)調(diào)用self的run:方法,并且傳遞參數(shù):@"abc" 
    2.   [self performSelector:@selector(run:) withObject:@"abc" afterDelay:3]; 

    SWIFT

    之前就已經(jīng)說過,Swift 里去掉了這個(gè)方法。

  • GCD

    可以使用 GCD 中的 dispatch_after 方法,OC 和 Swift 都可以使用,這里只寫 OC 的,Swift 的是一樣的。

    OBJECTIVE-C

    1. // 創(chuàng)建隊(duì)列 
    2. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    3. // 設(shè)置延時(shí),單位秒 
    4. double delay = 3;  
    5.  
    6. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), queue, ^{ 
    7.     // 3秒后需要執(zhí)行的任務(wù) 
    8. }); 
  • NSTimer

    NSTimer 是iOS中的一個(gè)計(jì)時(shí)器類,除了延遲執(zhí)行還有很多用法,不過這里直說延遲執(zhí)行的用法。同樣只寫 OC 版的,Swift 也是相同的。

    OBJECTIVE-C

    1. [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run:) userInfo:@"abc" repeats:NO]; 

單例模式

至于什么是單例模式,我也不多說,我只說說一般怎么實(shí)現(xiàn)。在 Objective-C 中,實(shí)現(xiàn)單例的方法已經(jīng)很具體了,雖然有別的方法,但是一般都是用一個(gè)標(biāo)準(zhǔn)的方法了,下面來看看。

OBJECTIVE-C

  1. @interface Tool : NSObject <NSCopying> 
  2.  
  3. + (instancetype)sharedTool; 
  4.  
  5. @end 
  6.  
  7. @implementation Tool 
  8.  
  9. static id _instance; 
  10.  
  11. + (instancetype)sharedTool { 
  12.     static dispatch_once_t onceToken; 
  13.     dispatch_once(&onceToken, ^{ 
  14.         _instance = [[Tool alloc] init]; 
  15.     }); 
  16.  
  17.     return _instance; 
  18.  
  19. @end 

這里之所以將單例模式,是因?yàn)槠渲杏玫搅?GCD 的 dispatch_once 方法。下面看 Swift 中的單例模式,在Swift中單例模式非常簡(jiǎn)單!想知道怎么從 OC 那么復(fù)雜的方法變成下面的寫法的,請(qǐng)看這里

SWIFT

  1. class Tool: NSObject { 
  2.     static let sharedTool = Tool() 
  3.  
  4.     // 私有化構(gòu)造方法,阻止其他對(duì)象使用這個(gè)類的默認(rèn)的'()'構(gòu)造方法 
  5.     private override init() {} 

從其他線程回到主線程的方法

我們都知道在其他線程操作完成后必須到主線程更新UI。所以,介紹完所有的多線程方案后,我們來看看有哪些方法可以回到主線程。

  • NSThread

    1. //Objective-C 
    2. [self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO]; 
    3.  
    4. //Swift 
    5. //swift 取消了 performSelector 方法。 
  • GCD

    1. //Objective-C 
    2. dispatch_async(dispatch_get_main_queue(), ^{ 
    3.  
    4. }); 
    5.  
    6. //Swift 
    7. dispatch_async(dispatch_get_main_queue(), { () -> Void in 
    8.  
    9. }) 
  • NSOperationQueue

    1. //Objective-C 
    2. [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
    3.  
    4. }]; 
    5.  
    6. //Swift 
    7. NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in 
    8.  

總結(jié)

好的吧,總算寫完了,純手敲6k多字,感動(dòng)死我了?;藘商?,時(shí)間跨度有點(diǎn)大,所以可能有些地方上段不接下段或者有的地方不完整,如果你看著比較費(fèi)力或者有什么地方有問題,都可以在評(píng)論區(qū)告訴我,我會(huì)及時(shí)修改的。當(dāng)然啦,多線程的東西也不止這些,題目也就只是個(gè)題目,不要當(dāng)真。想要了解更多的東西,還得自己去網(wǎng)上挖掘相關(guān)資料。多看看官方文檔。實(shí)在是編不下去了,大家好好看~。對(duì)了,看我寫的這么賣力,不打賞也得點(diǎn)個(gè)喜歡吧。

責(zé)任編輯:倪明 來源: 簡(jiǎn)書
相關(guān)推薦

2016-03-25 10:08:31

httpios開發(fā)

2017-04-10 15:47:50

Android Stujni開發(fā)入門

2024-09-27 11:51:33

Redis多線程單線程

2024-08-02 11:51:03

2013-07-16 10:12:14

iOS多線程多線程概念多線程入門

2015-10-10 10:10:20

2021-09-02 07:00:32

鑒權(quán)Web 應(yīng)用Cookie-sess

2020-09-09 12:55:28

Nginx高并發(fā)性能

2020-09-10 09:31:34

Nginx HTTP代理服務(wù)器

2019-08-20 14:40:35

Redis數(shù)據(jù)庫

2022-05-10 07:24:19

PythonCollection模塊

2017-03-10 21:04:04

Android適配

2017-03-07 15:35:26

Android適配 界面

2017-03-11 22:19:09

深度學(xué)習(xí)

2020-09-14 08:45:58

多線程模型面試

2011-08-02 10:26:59

iOS 多線程 線程

2015-07-22 09:51:51

iOS開發(fā)線程

2015-07-22 09:39:38

IOS多線程同步

2016-04-12 09:48:24

nsthread多線程ios

2019-07-10 15:15:23

JVM虛擬機(jī)Java
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)