iOS GCD詳解

  • 1恬口、GCD簡介

全名:Grand Central Dispatch,它是蘋果為多核的并行運(yùn)算提出的解決方案,會合理利用CPU户魏、自動管理線程的生命周期。使用時(shí)只需要在Block中寫入需要執(zhí)行的代碼即可挪挤。使用非常靈活绪抛。


  • 2、基本概念

    • 2.1电禀、串行與并發(fā)

      串行 :每次只有一個(gè)任務(wù)被執(zhí)行
      并發(fā):同一時(shí)間可以有多個(gè)任務(wù)被執(zhí)行
    • 2.2幢码、同步與異步

      同步:完成代碼塊后才返回,會阻塞當(dāng)前線程
      異步:代碼塊完成前就立即放回尖飞,不會阻塞當(dāng)前線程
    • 2.3症副、臨界區(qū)

      一段代碼不能被并發(fā)執(zhí)行店雅,兩個(gè)線程不能同時(shí)執(zhí)行這段代碼,否則這段代碼中的相關(guān)數(shù)據(jù)變得不可信贞铣。
    • 2.4闹啦、競態(tài)條件

      基于特定序列或時(shí)機(jī)的事件的軟件系統(tǒng)以不受控制的方式運(yùn)行的行為,可導(dǎo)致無法預(yù)測的行為辕坝,不能通過代碼檢查立即發(fā)現(xiàn)窍奋。
    • 2.5、死鎖

      多個(gè)東西因?yàn)榛ハ嗟却鵁o法完成酱畅,導(dǎo)致它們都卡住了琳袄。
    • 2.6、線程安全

      代碼能在多線程或并發(fā)任務(wù)中被安全的調(diào)用纺酸,而不會導(dǎo)致任何問題窖逗。
    • 2.7、上下文切換

      當(dāng)你的單個(gè)進(jìn)程里切換執(zhí)行不同的線程是存儲于恢復(fù)執(zhí)行狀態(tài)的過程

  • 3餐蔬、隊(duì)列

    • 主隊(duì)列:用于刷新UI碎紊,任何需要刷新UI的工作都要放在主隊(duì)列中執(zhí)行,同時(shí)為防止UI卡住樊诺,一般需要把耗時(shí)的任務(wù)放在其他線程中執(zhí)行

      dispatch_queue_t queue = dispatch_get_main_queue();
      
    • 全局隊(duì)列:一般并行任務(wù)都加入這個(gè)隊(duì)列中仗考,這是系統(tǒng)提供的一個(gè)全局的并發(fā)隊(duì)列

      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      

      獲取程序進(jìn)程缺省產(chǎn)生的并發(fā)隊(duì)列,可根據(jù)優(yōu)先級來選擇高痴鳄、中、低三個(gè)優(yōu)先級虽惭,
      由于這個(gè)是全局有系統(tǒng)控制的隊(duì)列芽唇,所以我們無法對其進(jìn)行 dispatch_resume() 繼續(xù) 和 dispatch_suspend() 中斷匆笤。

    • 自定義隊(duì)列:可以自定義串行隊(duì)列或者并行隊(duì)列

      //串行隊(duì)列
      dispatch_queue_t queue = dispatch_queue_create("demo", NULL);
      dispatch_queue_t queue = dispatch_queue_create("demo", DISPATCH_QUEUE_SERIAL);
      //并行隊(duì)列
      dispatch_queue_t queue = dispatch_queue_create("demo", DISPATCH_QUEUE_CONCURRENT);
      

      第一個(gè)參數(shù)是標(biāo)識符,用于 DEBUG 的時(shí)候標(biāo)識唯一的隊(duì)列咆课,可以為空
      第二個(gè)參數(shù)用來表示創(chuàng)建的隊(duì)列是串行的還是并行的书蚪,傳入 DISPATCH_QUEUE_SERIAL 或 NULL 表示創(chuàng)建串行隊(duì)列殊校。傳入 DISPATCH_QUEUE_CONCURRENT 表示創(chuàng)建并行隊(duì)列


  • 4为流、GCD使用

    • 4.1、死鎖問題:

      • 第一種:

        -(void)test1{
        NSLog(@"1");
        dispatch_sync(dispatch_get_main_queue(), ^{
           NSLog(@"2");
        });
        NSLog(@"3");
        } 
        

        輸出:

        2018-02-23 16:12:36.353985+0800 GCDDemo[24815:1940432] 1
        

        會卡在dispatch_sync(dispatch_get_main_queue(), ^{,或者報(bào)異常
        由于是主隊(duì)列同步執(zhí)行而且block是后下入主隊(duì)列的,所以block會放到主隊(duì)列的后面等待主隊(duì)列執(zhí)行完畢后再執(zhí)行静汤,所以2是放在3的后面的虫给。但是主線程也在等block執(zhí)行完畢抹估,這樣主線程才會繼續(xù)執(zhí)行瓷式。也就是說3又在等2執(zhí)行完畢才會執(zhí)行贸典。所以出現(xiàn)了死鎖

      • 第二種:

        -(void)test2{
         NSLog(@"1");
         dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
             NSLog(@"2");
         });
         NSLog(@"3");
        }
        

        輸出:

        2018-02-23 16:17:15.853493+0800 GCDDemo[24879:1950825] 1
        2018-02-23 16:17:15.853623+0800 GCDDemo[24879:1950825] 2
        2018-02-23 16:17:15.853715+0800 GCDDemo[24879:1950825] 3
        

        本次為同步全局隊(duì)列。雖然主線程隊(duì)列會等待2的執(zhí)行妒挎,但是2這次沒有放在3的后面而是在另一個(gè)全局隊(duì)列中酝掩,所以不會造成死鎖庸队。

      • 第三種:

         -(void)test3{
             NSLog(@"1");
             dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                 NSLog(@"2");
                 dispatch_sync(dispatch_get_main_queue(), ^{
                     NSLog(@"3");
                 });
                 NSLog(@"4");
             });
             NSLog(@"5");
         }
        

        輸出:

        2018-02-23 16:21:25.714923+0800 GCDDemo[24938:1961169] 1
        2018-02-23 16:21:25.715117+0800 GCDDemo[24938:1961169] 5
        2018-02-23 16:21:25.715124+0800 GCDDemo[24938:1961306] 2
        2018-02-23 16:21:25.718781+0800 GCDDemo[24938:1961169] 3
        2018-02-23 16:21:25.718926+0800 GCDDemo[24938:1961306] 4
        

        這里看似和第一種類似好像會造成死鎖彻消,其實(shí)不是的宾尚,這次主隊(duì)列其實(shí)已經(jīng)運(yùn)行完

      • 第四種:

         -(void)test4{
             NSLog(@"1");
             dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                 NSLog(@"2");
                 dispatch_sync(dispatch_get_main_queue(), ^{
                     NSLog(@"3");
                 });
                 NSLog(@"4");
             });
             NSLog(@"5");
             while (YES) {
                 
             }
             NSLog(@"6");
         }
        

        輸出:

        2018-02-23 16:26:49.901088+0800 GCDDemo[25017:1973298] 1
        2018-02-23 16:26:49.901234+0800 GCDDemo[25017:1973298] 5
        2018-02-23 16:26:49.901246+0800 GCDDemo[25017:1973398] 2
        

        主線程沒有執(zhí)行完,6沒打印煌贴,所以3還是放在主線程隊(duì)列后面,但是主線程沒有執(zhí)行完牛郑,不會執(zhí)行3。而3是同步的淹朋,所以3不執(zhí)行完4也不會打印

    • 4.2笙各、常見GCD操作:

      • 并發(fā)同步:

          -(void)test7{
              //并發(fā)同步
              NSLog(@"start");
              dispatch_queue_t queue = dispatch_queue_create("demo", DISPATCH_QUEUE_CONCURRENT);
              dispatch_sync(queue, ^{
                  for (int i = 0; i < 2; i++) {
                      NSLog(@"1----:%@",[NSThread currentThread]);
                  }
              });
              dispatch_sync(queue, ^{
                  for (int i = 0; i < 2; i++) {
                      NSLog(@"2----:%@",[NSThread currentThread]);
                  }
              });
              dispatch_sync(queue, ^{
                  for (int i = 0; i < 2; i++) {
                      NSLog(@"3----:%@",[NSThread currentThread]);
                  }
              });
              NSLog(@"end");
          }
        

        輸出:

          2018-02-23 16:36:18.856563+0800 GCDDemo[25130:1996747] start
          2018-02-23 16:36:18.856797+0800 GCDDemo[25130:1996747] 1----:<NSThread: 0x600000077040>{number = 1, name = main}
          2018-02-23 16:36:18.856986+0800 GCDDemo[25130:1996747] 1----:<NSThread: 0x600000077040>{number = 1, name = main}
          2018-02-23 16:36:18.857095+0800 GCDDemo[25130:1996747] 2----:<NSThread: 0x600000077040>{number = 1, name = main}
          2018-02-23 16:36:18.857388+0800 GCDDemo[25130:1996747] 2----:<NSThread: 0x600000077040>{number = 1, name = main}
          2018-02-23 16:36:18.857588+0800 GCDDemo[25130:1996747] 3----:<NSThread: 0x600000077040>{number = 1, name = main}
          2018-02-23 16:36:18.857749+0800 GCDDemo[25130:1996747] 3----:<NSThread: 0x600000077040>{number = 1, name = main}
          2018-02-23 16:36:18.857875+0800 GCDDemo[25130:1996747] end
        
      • 并發(fā)異步:

          -(void)test8{
              //并發(fā)異步
              NSLog(@"start");
              dispatch_queue_t queue = dispatch_queue_create("demo", DISPATCH_QUEUE_CONCURRENT);
              dispatch_async(queue, ^{
                  for (int i = 0; i < 2; i++) {
                      NSLog(@"1----:%@",[NSThread currentThread]);
                  }
              });
              dispatch_async(queue, ^{
                  for (int i = 0; i < 2; i++) {
                      NSLog(@"2----:%@",[NSThread currentThread]);
                  }
              });
              dispatch_async(queue, ^{
                  for (int i = 0; i < 2; i++) {
                      NSLog(@"3----:%@",[NSThread currentThread]);
                  }
              });
              NSLog(@"end");
          }
        

        輸出:

          2018-02-23 16:38:26.222795+0800 GCDDemo[25170:2002606] start
          2018-02-23 16:38:26.222964+0800 GCDDemo[25170:2002606] end
          2018-02-23 16:38:26.223061+0800 GCDDemo[25170:2002744] 1----:<NSThread: 0x600000267700>{number = 3, name = (null)}
          2018-02-23 16:38:26.223092+0800 GCDDemo[25170:2002747] 2----:<NSThread: 0x600000267640>{number = 4, name = (null)}
          2018-02-23 16:38:26.223104+0800 GCDDemo[25170:2002745] 3----:<NSThread: 0x604000465b00>{number = 5, name = (null)}
          2018-02-23 16:38:26.223231+0800 GCDDemo[25170:2002744] 1----:<NSThread: 0x600000267700>{number = 3, name = (null)}
          2018-02-23 16:38:26.223389+0800 GCDDemo[25170:2002747] 2----:<NSThread: 0x600000267640>{number = 4, name = (null)}
          2018-02-23 16:38:26.223480+0800 GCDDemo[25170:2002745] 3----:<NSThread: 0x604000465b00>{number = 5, name = (null)}
        
        
      • 串行同步:

          -(void)test9{
              //串行同步
              NSLog(@"start");
              dispatch_queue_t queue = dispatch_queue_create("demo", DISPATCH_QUEUE_SERIAL);
              dispatch_sync(queue, ^{
                  for (int i = 0; i < 2; i++) {
                      NSLog(@"1----:%@",[NSThread currentThread]);
                  }
              });
              dispatch_sync(queue, ^{
                  for (int i = 0; i < 2; i++) {
                      NSLog(@"2----:%@",[NSThread currentThread]);
                  }
              });
              dispatch_sync(queue, ^{
                  for (int i = 0; i < 2; i++) {
                      NSLog(@"3----:%@",[NSThread currentThread]);
                  }
              });
              NSLog(@"end");
          }
        

        輸出:

          2018-02-23 16:42:13.941307+0800 GCDDemo[25237:2012245] start
          2018-02-23 16:42:13.941525+0800 GCDDemo[25237:2012245] 1----:<NSThread: 0x600000065680>{number = 1, name = main}
          2018-02-23 16:42:13.941707+0800 GCDDemo[25237:2012245] 1----:<NSThread: 0x600000065680>{number = 1, name = main}
          2018-02-23 16:42:13.941940+0800 GCDDemo[25237:2012245] 2----:<NSThread: 0x600000065680>{number = 1, name = main}
          2018-02-23 16:42:13.942073+0800 GCDDemo[25237:2012245] 2----:<NSThread: 0x600000065680>{number = 1, name = main}
          2018-02-23 16:42:13.942181+0800 GCDDemo[25237:2012245] 3----:<NSThread: 0x600000065680>{number = 1, name = main}
          2018-02-23 16:42:13.942279+0800 GCDDemo[25237:2012245] 3----:<NSThread: 0x600000065680>{number = 1, name = main}
          2018-02-23 16:42:13.942372+0800 GCDDemo[25237:2012245] end
        
      • 串行異步:

          -(void)test10{
              //串行異步
              NSLog(@"start");
              dispatch_queue_t queue = dispatch_queue_create("demo", DISPATCH_QUEUE_SERIAL);
              dispatch_async(queue, ^{
                  for (int i = 0; i < 2; i++) {
                      NSLog(@"1----:%@",[NSThread currentThread]);
                  }
              });
              dispatch_async(queue, ^{
                  for (int i = 0; i < 2; i++) {
                      NSLog(@"2----:%@",[NSThread currentThread]);
                  }
              });
              dispatch_async(queue, ^{
                  for (int i = 0; i < 2; i++) {
                      NSLog(@"3----:%@",[NSThread currentThread]);
                  }
              });
              NSLog(@"end");
          }
        

        輸出:

          2018-02-23 16:43:05.320128+0800 GCDDemo[25263:2014958] start
          2018-02-23 16:43:05.320452+0800 GCDDemo[25263:2014958] end
          2018-02-23 16:43:05.320553+0800 GCDDemo[25263:2015026] 1----:<NSThread: 0x600000079d80>{number = 3, name = (null)}
          2018-02-23 16:43:05.321290+0800 GCDDemo[25263:2015026] 1----:<NSThread: 0x600000079d80>{number = 3, name = (null)}
          2018-02-23 16:43:05.322392+0800 GCDDemo[25263:2015026] 2----:<NSThread: 0x600000079d80>{number = 3, name = (null)}
          2018-02-23 16:43:05.322896+0800 GCDDemo[25263:2015026] 2----:<NSThread: 0x600000079d80>{number = 3, name = (null)}
          2018-02-23 16:43:05.323378+0800 GCDDemo[25263:2015026] 3----:<NSThread: 0x600000079d80>{number = 3, name = (null)}
          2018-02-23 16:43:05.323637+0800 GCDDemo[25263:2015026] 3----:<NSThread: 0x600000079d80>{number = 3, name = (null)}
        
  • 5、GCD線程之間的通訊

    在iOS開發(fā)過程中诊杆,我們一般在主線程里邊進(jìn)行UI刷新豹储,例如:點(diǎn)擊、滾動宰缤、拖拽等事件。我們通常把一些耗時(shí)的操作放在其他線程晃洒,比如說圖片下載慨灭、文件上傳等耗時(shí)操作。而當(dāng)我們有時(shí)候在其他線程完成了耗時(shí)操作時(shí)球及,需要回到主線程氧骤,那么就用到了線程之間的通訊。

      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          for (int i = 0; i < 2; ++i) {
              NSLog(@"1------%@",[NSThread currentThread]);
          }
    
          // 回到主線程
          dispatch_async(dispatch_get_main_queue(), ^{
              NSLog(@"2-------%@",[NSThread currentThread]);
          });
      });
    
  • 6吃引、 GCD的其他方法

    • 6.1筹陵、GCD的柵欄方法 dispatch_barrier_async

      我們有時(shí)需要異步執(zhí)行兩組操作刽锤,而且第一組操作執(zhí)行完之后,才能開始執(zhí)行第二組操作朦佩。這樣我們就需要一個(gè)相當(dāng)于柵欄一樣的一個(gè)方法將兩組異步執(zhí)行的操作組給分割起來并思,當(dāng)然這里的操作組里可以包含一個(gè)或多個(gè)任務(wù)。這就需要用到dispatch_barrier_async方法在兩個(gè)操作組間形成柵欄语稠。

      - (void)barrier
      {
          dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
      
          dispatch_async(queue, ^{
              NSLog(@"----1-----%@", [NSThread currentThread]);
          });
          dispatch_async(queue, ^{
              NSLog(@"----2-----%@", [NSThread currentThread]);
          });
      
          dispatch_barrier_async(queue, ^{
              NSLog(@"----barrier-----%@", [NSThread currentThread]);
          });
      
          dispatch_async(queue, ^{
              NSLog(@"----3-----%@", [NSThread currentThread]);
          });
          dispatch_async(queue, ^{
              NSLog(@"----4-----%@", [NSThread currentThread]);
          });
      }
      

      可以用來處理 讀者與寫者問題

    • 6.2宋彼、GCD的延時(shí)執(zhí)行方法 dispatch_after

      當(dāng)我們需要延遲執(zhí)行一段代碼時(shí),就需要用到GCD的dispatch_after方法仙畦。

      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2秒后異步執(zhí)行這里的代碼...
       NSLog(@"run-----");
      });
      
    • 6.3输涕、GCD的一次性代碼(只執(zhí)行一次) dispatch_once

      我們在創(chuàng)建單例、或者有整個(gè)程序運(yùn)行過程中只執(zhí)行一次的代碼時(shí)慨畸,我們就用到了GCD的dispatch_once方法莱坎。使用dispatch_once函數(shù)能保證某段代碼在程序運(yùn)行過程中只被執(zhí)行1次。

      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
          // 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)
      });
      
    • 6.4寸士、GCD的快速迭代方法 dispatch_apply

      通常我們會用for循環(huán)遍歷檐什,但是GCD給我們提供了快速迭代的方法dispatch_apply,使我們可以同時(shí)遍歷碉京。比如說遍歷0~5這6個(gè)數(shù)字厢汹,for循環(huán)的做法是每次取出一個(gè)元素,逐個(gè)遍歷谐宙。dispatch_apply可以同時(shí)遍歷多個(gè)數(shù)字烫葬。

      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      
      dispatch_apply(6, queue, ^(size_t index) {
        NSLog(@"%zd------%@",index, [NSThread currentThread]);
      });
      
    • 6.5、GCD的隊(duì)列組 dispatch_group

      有時(shí)候我們會有這樣的需求:分別異步執(zhí)行2個(gè)耗時(shí)操作凡蜻,然后當(dāng)2個(gè)耗時(shí)操作都執(zhí)行完畢后再回到主線程執(zhí)行操作搭综。這時(shí)候我們可以用到GCD的隊(duì)列組。

      • 我們可以先把任務(wù)放到隊(duì)列中划栓,然后將隊(duì)列放入隊(duì)列組中
      • 調(diào)用隊(duì)列組的dispatch_group_notify回到主線程執(zhí)行操作
      dispatch_group_t group =  dispatch_group_create();
      
      dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 執(zhí)行1個(gè)耗時(shí)的操作
            NSLog(@"耗時(shí)操作");
      });
      
      dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 執(zhí)行1個(gè)耗時(shí)的操作
            NSLog(@"耗時(shí)操作");
      });
      
      dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的異步操作都執(zhí)行完畢后兑巾,回到主線程...
      });
      

      也可以使用dispatch_group_enterdispatch_group_leave來通知 Dispatch Group 任務(wù)開始和完成,同時(shí)需要注意dispatch_group_enterdispatch_group_leave是成對出現(xiàn)忠荞。

  • 7蒋歌、 其他問題

    • 讀者與寫者問題

      在多線程中一個(gè)對象的數(shù)據(jù)被同時(shí)讀取和寫入將會導(dǎo)致數(shù)據(jù)不可信。例如委煤,多線程可以同時(shí)讀取NSMutableArray的一個(gè)實(shí)例而不會產(chǎn)生問題堂油,但是當(dāng)一個(gè)線程正在讀取時(shí),另一個(gè)線程正在修改數(shù)據(jù)就是不安全的碧绞。

      //寫入
      -(void)addObj:(id)obj{
          if(obj){
            [_objArray addObject:obj];
          }
      }
      //讀取
      -(NSArray *)array{
          return [NSArray arrayWithArray:_objArray];
      }
      

      這里所謂的方法府框,它讀取可變數(shù)據(jù)。它為調(diào)用者生成一個(gè)不可變的拷貝讥邻,防止調(diào)用者不當(dāng)?shù)母淖償?shù)組迫靖,但是這不能提供任何保護(hù)來對抗一個(gè)線程調(diào)用讀方法的同時(shí)另一個(gè)線程調(diào)用寫方法院峡。
      GCD通過用dispatch barriers創(chuàng)建一個(gè)讀寫鎖提供解決方案。
      Dispatch barriers 是一組函數(shù)系宜,在并發(fā)隊(duì)列上工作時(shí)扮演一個(gè)串行式的瓶頸照激。使用 GCD 的障礙(barrier)API 確保提交的 Block 在那個(gè)特定時(shí)間上是指定隊(duì)列上唯一被執(zhí)行的條目。這就意味著所有的先于調(diào)度障礙提交到隊(duì)列的條目必能在這個(gè) Block 執(zhí)行前完成蜈首。
      當(dāng)這個(gè) Block 的時(shí)機(jī)到達(dá)实抡,調(diào)度障礙執(zhí)行這個(gè) Block 并確保在那個(gè)時(shí)間里隊(duì)列不會執(zhí)行任何其它 Block 。一旦完成欢策,隊(duì)列就返回到它默認(rèn)的實(shí)現(xiàn)狀態(tài)吆寨。 GCD 提供了同步和異步兩種障礙函數(shù)。
      下面是你何時(shí)會——和不會——使用障礙函數(shù)的情況:

      • 自定義串行隊(duì)列:一個(gè)很壞的選擇踩寇;障礙不會有任何幫助啄清,因?yàn)椴还茉鯓犹季梗粋€(gè)串行隊(duì)列一次都只執(zhí)行一個(gè)操作喂柒。
      • 全局并發(fā)隊(duì)列:要小心;這可能不是最好的主意倦春,因?yàn)槠渌到y(tǒng)可能在使用隊(duì)列而且你不能壟斷它們只為你自己的目的睛榄。
      • 自定義并發(fā)隊(duì)列:這對于原子或臨界區(qū)代碼來說是極佳的選擇荣茫。任何你在設(shè)置或?qū)嵗男枰€程安全的事物都是使用障礙的最佳候選。

      由于上面唯一像樣的選擇是自定義并發(fā)隊(duì)列场靴,你將創(chuàng)建一個(gè)你自己的隊(duì)列去處理你的障礙函數(shù)并分開讀和寫函數(shù)啡莉。且這個(gè)并發(fā)隊(duì)列將允許多個(gè)多操作同時(shí)進(jìn)行。

      @interface SomeObject ()
      @property (nonatomic,strong,readonly) NSMutableArray *objArray;
      @property (nonatomic, strong) dispatch_queue_t concurrentPhotoQueue; ///< Add this
      @end
      
          -(id)init{
            ...
            _concurrentPhotoQueue = dispatch_queue_create("demo",
                                                      DISPATCH_QUEUE_CONCURRENT);
          }
      

      注意此處使用的是并發(fā)隊(duì)列旨剥。如果改為串行隊(duì)列咧欣,只能是單讀、單寫操作轨帜,在效率上會有折扣魄咕。
      修改寫入函數(shù)

      -(void)addObj:(id)obj
      {
          if (obj) { // 1
              dispatch_barrier_async(self.concurrentPhotoQueue, ^{ // 2 
                  [_objArray addObject:obj]; // 3
              });
          }
      }
      

      新寫的函數(shù)是這樣工作的:

      1. 在執(zhí)行下面所有的工作前檢查是否有合法的對象。
      2. 添加寫操作到你的自定義隊(duì)列蚌父。當(dāng)臨界區(qū)在稍后執(zhí)行時(shí)哮兰,這將是你隊(duì)列中唯一執(zhí)行的條目。
      3. 這是添加對象到數(shù)組的實(shí)際代碼苟弛。由于它是一個(gè)障礙 Block 喝滞,這個(gè) Block 永遠(yuǎn)不會同時(shí)和其它 Block 一起在 concurrentPhotoQueue 中執(zhí)行。

      修改讀取函數(shù)

      -(NSArray *)array
      {
          __block NSArray *retArray; // 1
          dispatch_sync(self.concurrentPhotoQueue, ^{ // 2
              retArray = [NSArray arrayWithArray:_objArray]; // 3
          });
          return retArray;
      }
      

      按順序看看編過號的注釋嗡午,有這些:

      1. __block 關(guān)鍵字允許對象在 Block 內(nèi)可變囤躁。沒有它冀痕,retArray 在 Block 內(nèi)部就只是只讀的荔睹,你的代碼甚至不能通過編譯狸演。
      2. 在 concurrentPhotoQueue 上同步調(diào)度來執(zhí)行讀操作。
      3. 將數(shù)組存儲在 retArray 內(nèi)并返回它僻他。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宵距,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子吨拗,更是在濱河造成了極大的恐慌满哪,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件劝篷,死亡現(xiàn)場離奇詭異哨鸭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)娇妓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門像鸡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人哈恰,你說我怎么就攤上這事只估。” “怎么了着绷?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵蛔钙,是天一觀的道長。 經(jīng)常有香客問我荠医,道長吁脱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任子漩,我火速辦了婚禮豫喧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘幢泼。我一直安慰自己紧显,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布缕棵。 她就那樣靜靜地躺著孵班,像睡著了一般。 火紅的嫁衣襯著肌膚如雪招驴。 梳的紋絲不亂的頭發(fā)上篙程,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機(jī)與錄音别厘,去河邊找鬼虱饿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的氮发。 我是一名探鬼主播渴肉,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼爽冕!你這毒婦竟也來了仇祭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤颈畸,失蹤者是張志新(化名)和其女友劉穎乌奇,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體眯娱,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡礁苗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了徙缴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寂屏。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖娜搂,靈堂內(nèi)的尸體忽然破棺而出迁霎,到底是詐尸還是另有隱情,我是刑警寧澤百宇,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布考廉,位于F島的核電站,受9級特大地震影響携御,放射性物質(zhì)發(fā)生泄漏昌粤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一啄刹、第九天 我趴在偏房一處隱蔽的房頂上張望涮坐。 院中可真熱鬧,春花似錦誓军、人聲如沸袱讹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捷雕。三九已至,卻和暖如春壹甥,著一層夾襖步出監(jiān)牢的瞬間救巷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工句柠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浦译,地道東北人棒假。 一個(gè)月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像精盅,于是被迫代替她去往敵國和親淆衷。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359