-
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_enter
和dispatch_group_leave
來通知 Dispatch Group 任務(wù)開始和完成,同時(shí)需要注意dispatch_group_enter
和dispatch_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ù)是這樣工作的:
- 在執(zhí)行下面所有的工作前檢查是否有合法的對象。
- 添加寫操作到你的自定義隊(duì)列蚌父。當(dāng)臨界區(qū)在稍后執(zhí)行時(shí)哮兰,這將是你隊(duì)列中唯一執(zhí)行的條目。
- 這是添加對象到數(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; }
按順序看看編過號的注釋嗡午,有這些:
- __block 關(guān)鍵字允許對象在 Block 內(nèi)可變囤躁。沒有它冀痕,retArray 在 Block 內(nèi)部就只是只讀的荔睹,你的代碼甚至不能通過編譯狸演。
- 在 concurrentPhotoQueue 上同步調(diào)度來執(zhí)行讀操作。
- 將數(shù)組存儲在 retArray 內(nèi)并返回它僻他。
-