iOS 并發(fā)編程 - Operation And NSOperation Queue

  • 基本概念

    1. 術(shù)語
    2. 串行 vs 并發(fā)(concurrency)
    3. 同步 vs 異步
    4. 隊列 vs 線程
  • iOS的并發(fā)編程模型

  • Operation Queues vs. Grand Central Dispatch (GCD)

  • 關(guān)于Operation對象

    1. 并發(fā)的Operation 和非并發(fā)的Operation
    2. 創(chuàng)建NSBlockOperation對象
    3. 創(chuàng)建NSInvocationOperation對象
  • 自定義Operation對象

    1. 自定義的非并發(fā)NSOperation-不實現(xiàn)取消操作
    2. 自定義的非并發(fā)NSOperation-實現(xiàn)取消操作
  • 定制Operation對象的執(zhí)行行為

    1. 修改Operation在隊列中的優(yōu)先級
    2. 修改Operation執(zhí)行任務(wù)線程的優(yōu)先級
    3. 設(shè)置Completion Block
  • 執(zhí)行Operation對象

    1. 添加Operation到Operation Queue中
    2. 手動執(zhí)行Operation
    3. 取消Operation
    4. 等待Operation執(zhí)行完成
    5. 暫停和恢復(fù)Operation Queue
  • 添加Operation Queue中Operation對象之間的依賴

  • 總結(jié)


看過上面的結(jié)構(gòu)預(yù)覽磨镶,下面就開始我們這篇blog

術(shù)語

Operation: The NSOperation class is an abstract class you use to encapsulate the code and data associated with a single task.

解釋:Operation是一個抽象類囊拜。你可以通過組織一段代碼和數(shù)據(jù),表示一個任務(wù)商模。

Operation Queue: The NSOperationQueue class regulates the execution of a set of NSOperation objects.

解釋: NSOperationQueue用于規(guī)則的去執(zhí)行一系列Operation。
任務(wù):通常的說是由一段代碼和數(shù)據(jù)組成做瞪,可以完成特定某項功能的代碼數(shù)據(jù)集合契讲。
進(jìn)程:進(jìn)程可以理解CPU所能執(zhí)行的單個任務(wù),CPU任何一個時刻職能運行一個進(jìn)程鹃栽。
線程:線程是計算機(jī)CPU所能執(zhí)行最小單元躏率,亦可以理解簡化版的進(jìn)程躯畴。一個進(jìn)程可以包含多個線程。

串行 vs 并發(fā)

最簡單的理解就是薇芝,串行和并發(fā)是用來修飾是否可以同時執(zhí)行任務(wù)的數(shù)量的蓬抄。串行設(shè)計只允許同一個時間段中只能一個任務(wù)在執(zhí)行。并發(fā)設(shè)計在同一個時間段中夯到,允許多個任務(wù)在邏輯上交織進(jìn)行嚷缭。(在iOS中,串行和并發(fā)一般用于描述隊列)
說個題外話耍贾,剛開始是將并發(fā)寫成并行的阅爽,后覺得并發(fā)和并行的概念一直揮之不去,可以參考這篇荐开,很贊奧——還在疑惑并發(fā)和并行付翁?

同步 vs 異步

同步操作,只有當(dāng)該操作執(zhí)行完成返回后晃听,才能執(zhí)行其他代碼百侧,會出現(xiàn)等待,易造成線程阻塞能扒。異步操作佣渴,不需要等到當(dāng)前操作執(zhí)行完,就可以返回初斑,執(zhí)行其他代碼辛润。(一般用于描述線程)

隊列 vs 線程

隊列用于存放Operation。在iOS中见秤,隊列分為串行隊列和并發(fā)隊列砂竖。使用NSOperationQueue時,我們不需要自己創(chuàng)建去創(chuàng)建線程鹃答,我們只需要自己去創(chuàng)建我們的任務(wù)(Operation)晦溪,將Operation放到隊列中去。隊列會負(fù)責(zé)去創(chuàng)建線程執(zhí)行挣跋,執(zhí)行完后三圆,會銷毀釋放線程占用的資源。


iOS并發(fā)編程模型

對于一個APP避咆,需要提高應(yīng)用的性能舟肉,一般需要創(chuàng)建輔助的線程去執(zhí)行任務(wù)。在整個APP的生命周期內(nèi)查库,我們需要自己手動去創(chuàng)建路媚,銷毀線程,以及暫停樊销,開啟線程整慎。對于這創(chuàng)建一個這樣的線程管理方案脏款,已經(jīng)是非常復(fù)雜且艱巨的任務(wù)。但是蘋果爸爸為開發(fā)者提供了兩套更好的解決方案:NSOperation裤园,Grand Central Dispatch (GCD) Reference撤师,GCD的方式具體的本文暫不討論。

使用NSOperationQueue 和 NSOperation的方式是蘋果基于GCD再一次封裝的一層拧揽,比GCD更加的靈活剃盾,而且是一種面向?qū)ο笤O(shè)計,更加適合開發(fā)人員淤袜。雖然相對于GCD會犧牲一些性能痒谴,但是我們可以對線程進(jìn)行更多的操作,比如暫停铡羡,取消积蔚,添加Operation間的依賴。但是GCD如果暫停和取消線程操作則十分的麻煩烦周。

Operation Queues vs. Grand Central Dispatch (GCD)

簡單來說库倘,GCD 是蘋果基于 C 語言開發(fā)的,一個用于多核編程的解決方案论矾,主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對稱多處理系統(tǒng)。而 Operation Queues 則是一個建立在 GCD 的基礎(chǔ)之上的杆勇,面向?qū)ο蟮慕鉀Q方案贪壳。它使用起來比 GCD 更加靈活,功能也更加強(qiáng)大蚜退。下面簡單地介紹了 Operation Queues 和 GCD 各自的使用場景:

Operation Queues :相對 GCD 來說闰靴,使用 Operation Queues 會增加一點點額外的開銷,但是我們卻換來了非常強(qiáng)大的靈活性和功能钻注,我們可以給 operation 之間添加依賴關(guān)系蚂且、取消一個正在執(zhí)行的 operation 、暫停和恢復(fù) operation queue 等幅恋;
GCD :則是一種更輕量級的杏死,以 FIFO 的順序執(zhí)行并發(fā)任務(wù)的方式,使用 GCD 時我們并不關(guān)心任務(wù)的 調(diào)度情況捆交,而讓系統(tǒng)幫我們自動處理淑翼。但是 GCD 的短板也是非常明顯的,比如我們想要給任務(wù)之間添加依賴關(guān)系品追、取消或者暫停一個正在執(zhí)行的任務(wù)時就會變得非常棘手玄括。

上引用自Operation Queues vs. Grand Central Dispatch (GCD)

關(guān)于Operation對象

NSOperation對象是一個抽象類,是不能直接創(chuàng)建對象的肉瓦。但是它有兩個子類——NSBlockOperation遭京,NSInvocationOperation.通常情況下我們都可以直接使用這兩個子類胃惜,創(chuàng)建可以并發(fā)的任務(wù)。

我們查看關(guān)于NSOperation.h的頭文件哪雕,可以發(fā)現(xiàn)任意的operation對象都可以自行開始任務(wù)(start)船殉,取消任務(wù)(cancle),以及添加依賴(addDependency:)和移除依賴(removeDependency:).關(guān)于依賴热监,有一種很好的一種開發(fā)思路捺弦。在operation對象中有很多屬性,可以用于檢測當(dāng)前任務(wù)的狀態(tài)孝扛,如isCancelled:是否已經(jīng)取消列吼,isFinished:是否已經(jīng)完成了任務(wù)。

屏幕快照 2016-06-07 下午8.29.04.png

  • 創(chuàng)建NSBlockOperation

以下使用到的代碼片段取自我的LSOperationAndOperationQueueDemo

NSBlockOperation顧名思義苦始,是是用block來創(chuàng)建任務(wù)寞钥,主要有兩種方式創(chuàng)建,一種是是用類方法陌选,一種是創(chuàng)建operation對象理郑,再添加任務(wù)。上代碼:以下代碼包括了兩種block創(chuàng)建任務(wù)的方式咨油。以及已經(jīng)有任務(wù)的operation對象再添加任務(wù)您炉。及直接添加任務(wù)到queue中。

@implementation LSBlockOperation

+ (LSBlockOperation *)lsBlockOperation {
    return [[LSBlockOperation alloc] init];
}

- (void)operatingLSBlockOperation {
    
    NSBlockOperation *blockOpt1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"-------- blockOpt1, mainThread:%@, currentThread:%@", [NSThread mainThread], [NSThread currentThread]);
    }];
    /// 繼續(xù)添加執(zhí)行的block
    [blockOpt1 addExecutionBlock:^{
        NSLog(@"-------- blockOpt1 addExecutionBlock1 mainThread:%@, currentThread:%@", [NSThread mainThread], [NSThread currentThread]);
    }];
    
    [blockOpt1 addExecutionBlock:^{
        NSLog(@"-------- blockOpt1 addExecutionBlock2 mainThread:%@, currentThread:%@", [NSThread mainThread], [NSThread currentThread]);
    }];
    
    NSBlockOperation *blockOpt2 = [[NSBlockOperation alloc] init];
    [blockOpt2 addExecutionBlock:^{
        NSLog(@"-------- blockOpt2 mainThread:%@, currentThread:%@", [NSThread mainThread], [NSThread currentThread]);
    }];
    
    NSBlockOperation *blockOpt3 = [[NSBlockOperation alloc] init];
    [blockOpt3 addExecutionBlock:^{
        NSLog(@"-------- blockOpt3 mainThread:%@, currentThread:%@", [NSThread mainThread], [NSThread currentThread]);
    }];
    
    NSBlockOperation *blockOpt4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"-------- blockOpt4 mainThread:%@, currentThread:%@", [NSThread mainThread], [NSThread currentThread]);
    }];
    
    // 添加執(zhí)行優(yōu)先級 - 并不能保證執(zhí)行順序
//    blockOpt2.queuePriority = NSOperationQueuePriorityVeryHigh;
//    blockOpt4.queuePriority = NSOperationQueuePriorityHigh;
    
    /// 可以設(shè)置Operation之間的依賴關(guān)系 - 執(zhí)行順序3 2 1 4
    [blockOpt2 addDependency:blockOpt3];
    [blockOpt1 addDependency:blockOpt2];
    [blockOpt4 addDependency:blockOpt1];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:blockOpt1];
    [queue addOperation:blockOpt2];
    [queue addOperation:blockOpt3];
    [queue addOperation:blockOpt4];
    [queue addOperationWithBlock:^{
        NSLog(@"-------- queue addOperationWithBlock1 mainThread:%@, currentThread:%@", [NSThread mainThread], [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"-------- queue addOperationWithBlock2 mainThread:%@, currentThread:%@", [NSThread mainThread], [NSThread currentThread]);
    }];
}
  • 創(chuàng)建NSInvocationOperation

NSInvocationOperation是另一種可創(chuàng)建的operation對象的類役电。但是在Swift中已經(jīng)被去掉了赚爵。NSInvocationOperation是一種可以非常靈活的創(chuàng)建任務(wù)的方式,主要是其中包含了一個targetselector法瑟。假設(shè)我們現(xiàn)在有一個任務(wù)冀膝,已經(jīng)在其它的類中寫好了,為了避免代碼的重復(fù)霎挟,我們可以將當(dāng)前的target指向為那個類對象窝剖,方法選擇器指定為那個方法即可,如果有參數(shù)酥夭,可以在NSInvocationOperation創(chuàng)建中指定對應(yīng)的Object(參數(shù)).

具體的可以看如下代碼:LSOperationAndOperationQueueDemo

@implementation LSInvocationOperation

+ (LSInvocationOperation *)lsInvocationOperation {
    return [[LSInvocationOperation alloc] init];
}

- (void)operationInvocationOperation {
    
    NSInvocationOperation *invoOpt1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invoOperated1) object:self];
    NSInvocationOperation *invoOpt2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invoOperated2) object:self];
    
    // invocated other obj method
    /// 可以執(zhí)行其它類中方法赐纱,并且可以帶參數(shù)
    NSInvocationOperation *invoOpt4 = [[NSInvocationOperation alloc] initWithTarget:[[Person alloc] init] selector:@selector(running:) object:@"linsir"];
    
    // 設(shè)置優(yōu)先級 - 并不能保證按指定順序執(zhí)行
//    invoOpt1.queuePriority = NSOperationQueuePriorityVeryLow;
//    invoOpt4.queuePriority = NSOperationQueuePriorityVeryLow;
//    invoOpt2.queuePriority = NSOperationQueuePriorityHigh;
    
    // 設(shè)置依賴 - 線性執(zhí)行
    [invoOpt1 addDependency:invoOpt2];
    [invoOpt2 addDependency:invoOpt4];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:invoOpt1];
    [queue addOperation:invoOpt2];
    [queue addOperation:invoOpt4];
}

- (void)invoOperated1 {
    NSLog(@"--------- invoOperated1, mainThread:%@, currentThread:%@", [NSThread mainThread],[NSThread currentThread]);
}

- (void)invoOperated2 {
    NSLog(@"--------- invoOperated2, mainThread:%@, currentThread:%@", [NSThread mainThread],[NSThread currentThread]);
}

@end

自定義Operation對象

上文介紹了兩種系統(tǒng)定義的NSOperation,通常情況下熬北,我們可以直接使用千所,已經(jīng)可以滿足了大部分的需求。但是當(dāng)系統(tǒng)的不能滿足時候蒜埋,我們就需要自定義我們自己的Operation對象淫痰。Operation對象可以分為并發(fā)的和非并發(fā)的兩類。從實現(xiàn)角度上而言整份,非并發(fā)的更容易實現(xiàn)的多待错。因為非并發(fā)的Operation對象中的很多屬性籽孙,它的父類已經(jīng)做好了管理,我們只需要直接使用就可以了火俄。(通常情況下犯建,實現(xiàn)多線程是由NSOperationQueue對象管理的,而不是NSOperation對象)實現(xiàn)自定義的NSOperation對象瓜客,最少需要重寫兩個方法适瓦,一個是初始化init方法(傳值),一個是mian方法(主要的邏輯實現(xiàn))谱仪。

  • 自定義的非并發(fā)NSOperation-不實現(xiàn)取消操作

    代碼片段取自LSOperationAndOperationQueueDemo

      @interface LSNonConcurrentOperation ()
    
      @property (nonatomic, strong)id data;
      
      @end
      
      /**
       自定義一個非并發(fā)的Operation玻熙,最少需要實現(xiàn)兩個方法,一個初始化的init方法疯攒,另一個是mian方法嗦随,即主方法,邏輯的主要執(zhí)行體敬尺。
       */
      @implementation LSNonConcurrentOperation
      
      - (id)initWithData:(id)data {
          self = [self init];
          if (self) {
              self.data = data;
          }
          return self;
      }
      
      // 該主方法不支持Operation的取消操作
      - (void)main {
          @try {
              
              NSLog(@"-------- LSNonConcurrentOperation - data:%@, mainThread:%@, currentThread:%@", self.data, [NSThread mainThread], [NSThread currentThread]);
              sleep(2);
              NSLog(@"-------- finish executed %@", NSStringFromSelector(_cmd));
              
          } @catch (NSException *exception) {
              
              NSLog(@"------- LSNonConcurrentOperation exception - %@", exception);
              
          } @finally {
              
          }
      }
    
  • 自定義的非并發(fā)NSOperation-實現(xiàn)取消操作

      - (void)main {
          // 執(zhí)行之前枚尼,檢查是否取消Operation
          if (self.isCancelled) return;
      
          @try {
              NSLog(@"-------- LSNonConcurrentOperation - data:%@, mainThread:%@, currentThread:%@", self.data, [NSThread mainThread], [NSThread currentThread]);
              
              // 循環(huán)去檢測執(zhí)行邏輯過程中是否取消當(dāng)前正在執(zhí)行的Operation
              for (NSInteger i = 0; i < 10000; i++) {
                  
                  NSLog(@"run loop -- %@", @(i + 1));
                  
                  if (self.isCancelled) return;
                  sleep(1);
              }
              NSLog(@"-------- finish executed %@", NSStringFromSelector(_cmd));
          } @catch (NSException *exception) {
              NSLog(@"------- LSNonConcurrentOperation exception - %@", exception);
              
          } @finally {
              
          }
      }
    

由上可以知道,取消一個任務(wù)的執(zhí)行砂吞,其實并不是立即就會取消署恍,而是會在一個runloop中不斷的去檢查,判斷isCancle的值蜻直,直到為yes時候盯质,則取消了操作。所以袭蝗,設(shè)置Operation為cancle的時候,至少需要一個runloop的時間才會結(jié)束操作般婆。

定制Operation對象的執(zhí)行行為

  • 修改Operation在隊列中的優(yōu)先級

NSOperation對象在Queue中可以設(shè)置執(zhí)行任務(wù)的優(yōu)先級到腥。我們可以通過設(shè)置operation對象的setQueuePriority:方法,改變?nèi)蝿?wù)在隊列中的執(zhí)行優(yōu)先級蔚袍。但是真正決定一個operation對象能否執(zhí)行的是isReady乡范,假設(shè)一個operation對象的在隊列執(zhí)行的優(yōu)先級很高,另一個很低啤咽,但是高的operation對象的isReady是NO晋辆,也只會執(zhí)行優(yōu)先級低的operation任務(wù)。另一個影響任務(wù)在隊列中執(zhí)行順序的是依賴(下文會講到)宇整,假設(shè)operation A依賴于operation B瓶佳,所以一定先執(zhí)行operation B,再執(zhí)行operation A.

  • 修改Operation執(zhí)行任務(wù)線程的優(yōu)先級

從iOS4.0開始,我們可以設(shè)置operation中任務(wù)執(zhí)行的線程優(yōu)先級鳞青。從iOS4.0到iOS8.0霸饲,operation對象可以通過方法setThreadPriority:为朋,這里的參數(shù)是一個double類型,范圍是0.0到1.0厚脉,設(shè)置越高习寸,理論上講,線程執(zhí)行的可能性就越高傻工。但是從iOS8.0之后霞溪,這個方法已經(jīng)被廢棄了,使用setQualityOfService:代替中捆,這里參數(shù)是一個預(yù)設(shè)的枚舉值鸯匹。

  • 設(shè)置Completion Block

同上,從iOS4.0開始轨香,可以給每個operation對象設(shè)置一個主任務(wù)完成之后的完成回調(diào)setCompletionBlock:忽你。所設(shè)置的block執(zhí)行是在檢測到operation的isFinished為YES后執(zhí)行的。值得注意的是:我們并不能保證block所在的線程一定在主線程臂容,所以當(dāng)我們需要對主線程上做一些操作的時候科雳,我們應(yīng)該切換線程到主線程中,如需在其他線程執(zhí)行的某些操作脓杉,亦需要切換線程糟秘。

Therefore, you should not use this block to do any work that requires a very specific execution context. Instead, you should shunt that work to your application’s main thread or to the specific thread that is capable of doing it.

執(zhí)行Operation對象

對于執(zhí)行一個Operation對象,一般的做法是將operation對象添加到一個隊列中去球散,之后隊列會根據(jù)當(dāng)前系統(tǒng)的狀態(tài)尿赚,以及內(nèi)核的狀態(tài),自行的去執(zhí)行operation中的任務(wù)蕉堰。

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:opt];

還有一種做法是凌净,我們可以手動的執(zhí)行一個operation對象,直接調(diào)用operation的start方法

[opt start];
  • 添加Operation到Operation Queue中

將operation對象添加到queue中非常簡單

首先創(chuàng)建一個隊列:

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

添加到隊列的方法如下:

- (void)addOperation:(NSOperation *)op;
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);
- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);

第一個:添加意境存在的operation對象
第二個:添加一組operation對象
第三個:直接添加一個block到隊列中屋讶,無需創(chuàng)建operation對象

  • 手動執(zhí)行Operation

一般情況下冰寻,我們不需要手動的去執(zhí)行一個operation對象,但如果需要皿渗,亦可斩芭,調(diào)用start方法。

[opt start];
  • 取消Operation

當(dāng)我們將一個operation對象添加到隊列中之后乐疆,operation就已經(jīng)被隊列所擁有划乖。我們可以在某個需要的時候調(diào)用operation對象的cancle方法,將operation出列挤土。并且此時operation的isFinished也會為YES琴庵,所以此時依賴于它的operation就回繼續(xù)得到執(zhí)行。當(dāng)然,我們可以直接調(diào)用隊列的cancelAllOperations方法细卧,取消了隊列中所有的operation執(zhí)行尉桩。

  • 等待Operation執(zhí)行完成

等待一個Operation對象的執(zhí)行完成,可以使用waitUntilFinished方法贪庙。但是應(yīng)該注意到蜘犁,等待一個任務(wù)執(zhí)行完,會阻塞當(dāng)前線程止邮。所以我們絕不應(yīng)該在主線程中做該操作这橙,那樣會帶來非常差的體驗。所以該操作應(yīng)該使用輔助線程中导披。
我們也可以調(diào)用NSOperationQueue對象的waitUntilAllOperationsAreFinished方法屈扎,知道所有的任務(wù)都執(zhí)行完成。

  • 暫停和恢復(fù)Operation Queue

通過設(shè)置隊列的setSuspended,我們可以暫停一個隊列中還沒有開始執(zhí)行的operation對象撩匕,對于已經(jīng)開始的執(zhí)行的任務(wù)鹰晨,將繼續(xù)執(zhí)行。并且止毕,已經(jīng)暫停了隊列模蜡,仍然可以繼續(xù)添加operation對象,但是不會執(zhí)行扁凛,只能等到從暫停(掛起)狀態(tài)切換到非暫停狀態(tài)忍疾。即設(shè)置setSuspended為NO。對于單個的operation谨朝,是沒有暫停的概念的卤妒。

When the value of this property is NO, the queue actively starts operations that are in the queue and ready to execute. Setting this property to YES prevents the queue from starting any queued operations, but already executing operations continue to execute. You may continue to add operations to a queue that is suspended but those operations are not scheduled for execution until you change this property to NO.

添加Operation Queue中Operation對象之間的依賴

NSOperationQueue中,如果沒有經(jīng)過對operation添加依賴字币,都是使用并發(fā)處理的则披。但是在某些情況下,我們對任務(wù)的執(zhí)行是有非常嚴(yán)格的規(guī)定的洗出。即需要串行執(zhí)行士复,此時,我們就需要對operation對象間進(jìn)行添加依賴處理共苛。

- (void)addDependency:(NSOperation *)op;
- (void)removeDependency:(NSOperation *)op;

第一個:添加依賴
第二個:移除依賴

依賴判没,是一種非常好用功能蜓萄,在我們做項目(生活中)的時候隅茎,很多時候都一種依賴的概念。比如嫉沽,用戶需要上傳一張照片到自己的空間辟犀,但是此時必須檢測該用戶是否已經(jīng)登錄。以前我們可能將兩個邏輯寫在一起绸硕,但是現(xiàn)在可以將成寫成兩個不同的operation堂竟,并設(shè)置它們的依賴魂毁。這樣的好處非常可見的:
第一點:它可以幫我們解藕出嘹,不同的邏輯分在不一樣的對象中席楚。
第二點:某些常用的邏輯會經(jīng)常用到,以后不需要一次次的重復(fù)税稼,可讀性增強(qiáng)烦秩,以后需要的時候直接調(diào)用,設(shè)置其依賴即可郎仆。比如檢測是否登錄

總結(jié)

多線程執(zhí)行任務(wù)看似十分的復(fù)雜只祠,但是如果將復(fù)雜的任務(wù)交給NSOperation and NSOperationQueue,就可以簡化它的難度扰肌,并且它似乎可以比我們自己處理的更好抛寝。

文中使用的demo - LSOperationAndOperationQueueDemo

感謝

以下文章給我?guī)矸浅4蟮膸椭?/p>

還在疑惑并發(fā)和并行?
進(jìn)程與線程的一個簡單解釋
iOS 并發(fā)編程之 Operation Queues
NSOperationQueue - 文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末曙旭,一起剝皮案震驚了整個濱河市盗舰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌夷狰,老刑警劉巖岭皂,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異沼头,居然都是意外死亡爷绘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門进倍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來土至,“玉大人,你說我怎么就攤上這事猾昆√找颍” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵垂蜗,是天一觀的道長楷扬。 經(jīng)常有香客問我,道長贴见,這世上最難降的妖魔是什么烘苹? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮片部,結(jié)果婚禮上镣衡,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好廊鸥,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布望浩。 她就那樣靜靜地躺著,像睡著了一般惰说。 火紅的嫁衣襯著肌膚如雪磨德。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天吆视,我揣著相機(jī)與錄音剖张,去河邊找鬼。 笑死揩环,一個胖子當(dāng)著我的面吹牛搔弄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丰滑,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼顾犹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了褒墨?” 一聲冷哼從身側(cè)響起炫刷,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎郁妈,沒想到半個月后浑玛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡噩咪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年顾彰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胃碾。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡涨享,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仆百,到底是詐尸還是另有隱情厕隧,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布俄周,位于F島的核電站吁讨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏峦朗。R本人自食惡果不足惜建丧,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望甚垦。 院中可真熱鬧茶鹃,春花似錦、人聲如沸艰亮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽迄埃。三九已至疗韵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間侄非,已是汗流浹背蕉汪。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留逞怨,地道東北人者疤。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像叠赦,于是被迫代替她去往敵國和親驹马。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容