-
什么是進(jìn)程?
進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序
每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專(zhuān)用且受保護(hù)的內(nèi)存空間內(nèi)
-
比如同時(shí)打開(kāi)酷狗责球、Xcode焦履,系統(tǒng)就會(huì)分別啟動(dòng)2個(gè)進(jìn)程
-
什么是線(xiàn)程?
- 1個(gè)進(jìn)程要想執(zhí)行任務(wù),必須得有線(xiàn)程(每1個(gè)進(jìn)程至少要有1條線(xiàn)程)
- 一個(gè)進(jìn)程(程序)的所有任務(wù)都在線(xiàn)程中執(zhí)行
-
什么是多線(xiàn)程?
- 1個(gè)進(jìn)程中可以開(kāi)啟多條線(xiàn)程雏逾,多條線(xiàn)程可以并行(同時(shí))執(zhí)行不同的任務(wù)
- 多線(xiàn)程技術(shù)可以提高程序的執(zhí)行效率
- eg: 進(jìn)程-->車(chē)間嘉裤,線(xiàn)程 -->車(chē)間工人(--> 是 “相當(dāng)于” “比作”的意思)
- 多線(xiàn)程的優(yōu)點(diǎn)
1.能適當(dāng)提高程序的執(zhí)行效率
2.能適當(dāng)提高資源利用率(CPU、內(nèi)存利用率)
- 多線(xiàn)程的缺點(diǎn)
1.創(chuàng)建線(xiàn)程是有開(kāi)銷(xiāo)的栖博,iOS下主要成本包括:內(nèi)核數(shù)據(jù)結(jié)構(gòu)(大約1KB)屑宠、棧空間(子線(xiàn)程512KB仇让、主線(xiàn)程1MB典奉,也可以使用-setStackSize:設(shè)置躺翻,但必須是4K的倍數(shù),而且最小是16K)秋柄,創(chuàng)建線(xiàn)程大約需要90毫秒的創(chuàng)建時(shí)間
2.如果開(kāi)啟大量的線(xiàn)程获枝,會(huì)降低程序的性能
3.線(xiàn)程越多,CPU在調(diào)度線(xiàn)程上的開(kāi)銷(xiāo)就越大
4.程序設(shè)計(jì)更加復(fù)雜:比如線(xiàn)程之間的通信骇笔、多線(xiàn)程的數(shù)據(jù)共享
多線(xiàn)程在iOS開(kāi)發(fā)中的應(yīng)用
-
什么是主線(xiàn)程?
一個(gè)iOS程序運(yùn)行后,默認(rèn)會(huì)開(kāi)啟1條線(xiàn)程嚣崭,稱(chēng)為“主線(xiàn)程”或“UI線(xiàn)程”
-
主線(xiàn)程
- 顯示/刷新UI界面
- 處理UI事件(比如點(diǎn)擊事件笨触、滾動(dòng)事件、拖拽事件等)
-
主線(xiàn)程的使用注意
- 別將比較耗時(shí)的操作放到主線(xiàn)程中
-
耗時(shí)操作會(huì)卡住主線(xiàn)程雹舀,嚴(yán)重影響UI的流暢度芦劣,給用戶(hù)一種“卡”的壞體驗(yàn)
iOS中多線(xiàn)程的實(shí)現(xiàn)方案
| 技術(shù)方案 | 簡(jiǎn)介 | 語(yǔ)言 | 線(xiàn)程生命周期 | 使用頻率 |
| -------- | -----: | :----: | | :----: | | :----: |
| 1.pthread | 1.一套通用的多線(xiàn)程API。 2.適用于Unix\Linux\Windows等系統(tǒng)说榆。 3.跨平臺(tái)\可移植虚吟。 4.使用難度大 | C | 程序員管理 | 幾乎不用 |
| 2.NSThread | 1.使用更加面向?qū)ο?。 2.簡(jiǎn)單易用签财,可直接操作線(xiàn)程對(duì)象 | OC | 程序員管理 | 偶爾使用 |
| 3.GCD |1.旨在替代NSThread等線(xiàn)程技術(shù)串慰。2.充分利用設(shè)備的多核 | C | 自動(dòng)管理 | 經(jīng)常使用 |
| 4.NSOperation | 1.基于GCD(底層是GCD)。2.比GCD多了一些更簡(jiǎn)單實(shí)用的功能唱蒸。3.使用更加面向?qū)ο? | OC | 自動(dòng)管理 | 經(jīng)常使用 |
1.pthread(了解)
- 創(chuàng)建pthread
- pthread_create
- 只要create一次就會(huì)創(chuàng)建一個(gè)新的線(xiàn)程
- 系統(tǒng)會(huì)自動(dòng)在子線(xiàn)程中調(diào)用傳入的函數(shù)
/*
第一個(gè)參數(shù): 線(xiàn)程的代號(hào)(當(dāng)做就是線(xiàn)程)
第二個(gè)參數(shù): 線(xiàn)程的屬性
第三個(gè)參數(shù): 指向函數(shù)的指針, 就是將來(lái)線(xiàn)程需要執(zhí)行的方法
第四個(gè)參數(shù): 給第三個(gè)參數(shù)的指向函數(shù)的指針 傳遞的參數(shù)
void *(*functionP)(void *)
void * == id
一般情況下C語(yǔ)言中的類(lèi)型都是以 _t或者Ref結(jié)尾
*/
pthread_t threadId;
// 只要create一次就會(huì)創(chuàng)建一個(gè)新的線(xiàn)程
pthread_create(&threadId , NULL, &demo, "lym");
2. NSThread(了解/掌握)
- 一個(gè)NSThread對(duì)象就代表一條線(xiàn)程
- 幾種創(chuàng)建方式
- 第一種
- alloc + init
- 注意: 需要手動(dòng)啟動(dòng)線(xiàn)程
- 特點(diǎn): 系統(tǒng)內(nèi)部會(huì)retain當(dāng)前線(xiàn)程
- 只有線(xiàn)程中的方法執(zhí)行完畢, 系統(tǒng)才會(huì)將其釋放
// 第一種創(chuàng)建方式
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];
// 線(xiàn)程一啟動(dòng)邦鲫,就會(huì)在線(xiàn)程thread中執(zhí)行self的run方法
- 第二種
- 不用手動(dòng)調(diào)用start方法, 系統(tǒng)會(huì)自動(dòng)啟動(dòng)
- 沒(méi)有返回值, 不能對(duì)線(xiàn)程進(jìn)行更多的設(shè)置
- 應(yīng)用場(chǎng)景: 需要快速簡(jiǎn)便的執(zhí)行線(xiàn)程
// 第二種創(chuàng)建方式
// 創(chuàng)建線(xiàn)程后自動(dòng)啟動(dòng)線(xiàn)程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
// 系統(tǒng)就會(huì)自動(dòng)創(chuàng)建一個(gè)子線(xiàn)程, 并且在子線(xiàn)程中自動(dòng)執(zhí)行self的@selector方法(隱式創(chuàng)建并啟動(dòng)線(xiàn)程)
[self performSelectorInBackground:@selector(run) withObject:nil];
/**
上述2種創(chuàng)建線(xiàn)程方式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):簡(jiǎn)單快捷
缺點(diǎn):無(wú)法對(duì)線(xiàn)程進(jìn)行更詳細(xì)的設(shè)置
*/
- 控制線(xiàn)程狀態(tài)
- 創(chuàng)建出來(lái) -> 新建狀態(tài)
- 調(diào)用start -> 準(zhǔn)備就緒
- 被CPU調(diào)用 -> 運(yùn)行
- sleep -> 阻塞
- 執(zhí)行完畢, 或者被強(qiáng)制關(guān)閉 -> 死亡
-
注意: 如果強(qiáng)制關(guān)閉線(xiàn)程, 關(guān)閉之后的其它操作都無(wú)法執(zhí)行
-
// 啟動(dòng)線(xiàn)程
//進(jìn)入就緒狀態(tài) -> 運(yùn)行狀態(tài)。當(dāng)線(xiàn)程任務(wù)執(zhí)行完畢神汹,自動(dòng)進(jìn)入死亡狀態(tài)
-(void)start;
阻塞(暫停)線(xiàn)程
//進(jìn)入阻塞狀態(tài)
+(void)sleepUntilDate:(NSDate *)date;
+(void)sleepForTimeInterval:(NSTimeInterval)time;
強(qiáng)制停止線(xiàn)程
//進(jìn)入死亡狀態(tài)
+(void)exit;
###warm 注意:一旦線(xiàn)程停止(死亡)了庆捺,就不能再次開(kāi)啟任務(wù)```
- 主線(xiàn)程相關(guān)用法
+(NSThread*)mainThread; // 獲得主線(xiàn)程
-(BOOL)isMainThread; // 是否為主線(xiàn)程
+(BOOL)isMainThread; // 是否為主線(xiàn)程
NSThread *current = [NSThread currentThread]; // 獲得當(dāng)前線(xiàn)程
-(void)setName:(NSString *)name;// setter線(xiàn)程的名字
-(NSString *)name; // getter線(xiàn)程的名字```
-
多線(xiàn)程的安全隱患
- 資源共享
- 塊資源可能會(huì)被多個(gè)線(xiàn)程共享,也就是多個(gè)線(xiàn)程可能會(huì)訪(fǎng)問(wèn)同一塊資源
- 比如多個(gè)線(xiàn)程訪(fǎng)問(wèn)同一個(gè)對(duì)象屁魏、同一個(gè)變量滔以、同一個(gè)文件
-
當(dāng)多個(gè)線(xiàn)程訪(fǎng)問(wèn)同一塊資源時(shí),很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問(wèn)題
- 資源共享
-
安全隱患解決– 互斥鎖
- 互斥鎖使用格式
@synchronized(鎖對(duì)象)
{ // 需要鎖定的代碼 }
注意:鎖定1份代碼只用1把鎖氓拼,用多把鎖是無(wú)效的```
- 互斥鎖的優(yōu)缺點(diǎn)
>- 優(yōu)點(diǎn):能有效防止因多線(xiàn)程搶奪資源造成的數(shù)據(jù)安全問(wèn)題
- 缺點(diǎn):需要消耗大量的CPU資源
- 互斥鎖的使用前提:多條線(xiàn)程搶奪同一塊資源
- 相關(guān)專(zhuān)業(yè)術(shù)語(yǔ):線(xiàn)程同步
>- 線(xiàn)程同步的意思是:多條線(xiàn)程在同一條線(xiàn)上執(zhí)行(按順序地執(zhí)行任務(wù))
- 互斥鎖你画,就是使用了線(xiàn)程同步技術(shù)
- ####NSThread線(xiàn)程間通信
- 什么叫做線(xiàn)程間通信
- 在1個(gè)進(jìn)程中,線(xiàn)程往往不是孤立存在的披诗,多個(gè)線(xiàn)程之間需要經(jīng)常進(jìn)行通信
- 線(xiàn)程間通信的體現(xiàn)
- 1個(gè)線(xiàn)程傳遞數(shù)據(jù)給另1個(gè)線(xiàn)程
- 在1個(gè)線(xiàn)程中執(zhí)行完特定任務(wù)后撬即,轉(zhuǎn)到另1個(gè)線(xiàn)程繼續(xù)執(zhí)行任務(wù)
- 線(xiàn)程間通信常用方法
-(void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
-(void)performSelector:(SEL)aSelector onThread:(NSThread *)thread withObject:(id)arg waitUntilDone:(BOOL)wait;```
performSelectorOnMainThread方法中waitUntilDone:NO參數(shù)的含義
如果傳入的是YES: 那么會(huì)等到主線(xiàn)程中的方法執(zhí)行完畢, 才會(huì)繼續(xù)執(zhí)行下面其他行的代碼
如果傳入的是NO: 那么不用等到主線(xiàn)程中的方法執(zhí)行完畢, 就可以繼續(xù)執(zhí)行下面其他行的代碼
注意點(diǎn): 更新UI一定要在主線(xiàn)程中更新
[self performSelectorInBackground:@selector(download2:) withObject:url];
[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:NO];
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
[self performSelector:@selector(showImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
擴(kuò)展
-
原子和非原子屬性
- atomic:原子屬性,為setter方法加鎖(默認(rèn)就是atomic)
- nonatomic:非原子屬性呈队,不會(huì)為setter方法加鎖
- 注意點(diǎn): atomic系統(tǒng)自動(dòng)給我們添加的鎖不是互斥鎖/ 自旋鎖
-
自旋鎖和互斥鎖對(duì)比
- 共同點(diǎn)
- 都能夠保證多線(xiàn)程在同一時(shí)候, 只能有一個(gè)線(xiàn)程操作鎖定的代碼
- 不同點(diǎn)
- 如果是互斥鎖, 假如現(xiàn)在被鎖住了, 那么后面來(lái)得線(xiàn)程就會(huì)進(jìn)入”休眠”狀態(tài), 直到解鎖之后, 又會(huì)喚醒線(xiàn)程繼續(xù)執(zhí)行
- 如果是自旋鎖, 假如現(xiàn)在被鎖住了, 那么后面來(lái)得線(xiàn)程不會(huì)進(jìn)入休眠狀態(tài), 會(huì)一直傻傻的等待, 直到解鎖之后立刻執(zhí)行
- 自旋鎖更適合做一些較短的操作
- 共同點(diǎn)
3.GCD(掌握/理解)
什么是GCD
全稱(chēng)是Grand Central Dispatch剥槐,可譯為“牛逼的中樞調(diào)度器”
純C語(yǔ)言,提供了非常多強(qiáng)大的函數(shù)
- GCD的優(yōu)勢(shì)
- GCD是蘋(píng)果公司為多核的并行運(yùn)算提出的解決方案
GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核宪摧、四核)
GCD會(huì)自動(dòng)管理線(xiàn)程的生命周期(創(chuàng)建線(xiàn)程粒竖、調(diào)度任務(wù)颅崩、銷(xiāo)毀線(xiàn)程)
程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫(xiě)任何線(xiàn)程管理代碼
GCD中有2個(gè)核心概念
- 任務(wù):執(zhí)行什么操作
隊(duì)列:用來(lái)存放任務(wù)
-
GCD的使用就2個(gè)步驟
- 定制任務(wù)
- 確定想做的事情
- 將任務(wù)添加到隊(duì)列中
- GCD會(huì)自動(dòng)將隊(duì)列中的任務(wù)取出蕊苗,放到對(duì)應(yīng)的線(xiàn)程中執(zhí)行
- 任務(wù)的取出遵循隊(duì)列的FIFO原則:先進(jìn)先出沿后,后進(jìn)后出
-
如何執(zhí)行任務(wù)
- 同步函數(shù)dispatch_sync
- 不具備開(kāi)啟新線(xiàn)程的能力
- 異步函數(shù)dispatch_async
- 具備開(kāi)啟新線(xiàn)程的能力
- 同步和異步主要影響:能不能開(kāi)啟新的線(xiàn)程
-
隊(duì)列的類(lèi)型
- 并發(fā)隊(duì)列(Concurrent Dispatch Queue)
- 可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開(kāi)啟多個(gè)線(xiàn)程同時(shí)執(zhí)行任務(wù))
- 并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
- 自己創(chuàng)建: dispatch_queue_t queue = dispatch_queue_create("com.Mg.lym", DISPATCH_QUEUE_CONCURRENT);
- 全局并發(fā)隊(duì)列 : dispatch_get_global_queue(0 , 0);
###使用dispatch_queue_create函數(shù)創(chuàng)建隊(duì)列
dispatch_queue_t
dispatch_queue_create(const char *label, // 隊(duì)列名稱(chēng)
dispatch_queue_attr_t attr); // 隊(duì)列的類(lèi)型
dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_CONCURRENT);
###GCD默認(rèn)已經(jīng)提供了全局的并發(fā)隊(duì)列,供整個(gè)應(yīng)用使用朽砰,可以無(wú)需手動(dòng)創(chuàng)建
使用dispatch_get_global_queue函數(shù)獲得全局的并發(fā)隊(duì)列
dispatch_queue_t
dispatch_get_global_queue(
dispatch_queue_priority_t priority, // 隊(duì)列的優(yōu)先級(jí)
unsigned long flags); // 此參數(shù)暫時(shí)無(wú)用尖滚,用0即可
###獲得全局并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
###全局并發(fā)隊(duì)列的優(yōu)先級(jí)
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默認(rèn)(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后臺(tái)```
***
>- 串行隊(duì)列(Serial Dispatch Queue)
- 讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行(一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù))
GCD中獲得串行有2種途徑
使用dispatch_queue_create函數(shù)創(chuàng)建串行隊(duì)列
// 創(chuàng)建串行隊(duì)列(隊(duì)列類(lèi)型傳遞NULL或者DISPATCH_QUEUE_SERIAL)
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue",NULL);
使用主隊(duì)列(跟主線(xiàn)程相關(guān)聯(lián)的隊(duì)列)
主隊(duì)列是GCD自帶的一種特殊的串行隊(duì)列
放在主隊(duì)列中的任務(wù)瞧柔,都會(huì)放到主線(xiàn)程中執(zhí)行
使用dispatch_get_main_queue()獲得主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();```
-
有4個(gè)術(shù)語(yǔ)比較容易混淆:同步漆弄、異步、并發(fā)造锅、串行
- 同步和異步主要影響:能不能開(kāi)啟新的線(xiàn)程
- 同步:只是在當(dāng)前線(xiàn)程中執(zhí)行任務(wù)撼唾,不具備開(kāi)啟新線(xiàn)程的能力
- 異步:可以在新的線(xiàn)程中執(zhí)行任務(wù),具備開(kāi)啟新線(xiàn)程的能力
- 并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式
- 并發(fā):允許多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行
- 串行:一個(gè)任務(wù)執(zhí)行完畢后哥蔚,再執(zhí)行下一個(gè)任務(wù)
-
GCD的各種組合
- 異步 + 并行 = 會(huì)開(kāi)啟新的線(xiàn)程
- 異步函數(shù), 會(huì)先執(zhí)行完所有的代碼, 再在子線(xiàn)程中執(zhí)行任務(wù)
- 異步 + 串行 = 會(huì)創(chuàng)建新的線(xiàn)程, 但是只會(huì)創(chuàng)建一個(gè)新的線(xiàn)程, 所有的任務(wù)都在這一個(gè)新的線(xiàn)程中執(zhí)行
- 同步 + 并行 = 不會(huì)開(kāi)啟新的線(xiàn)程
- 其實(shí)就相當(dāng)于同步 + 串行
- 同步函數(shù), 只要代碼執(zhí)行到了同步函數(shù)的那一行, 就會(huì)立即執(zhí)行任務(wù), 只有任務(wù)執(zhí)行完畢才會(huì)繼續(xù)往后執(zhí)行
- 同步 + 串行 = 不會(huì)創(chuàng)建新的線(xiàn)程
- 異步 + 主隊(duì)列 = 不會(huì)開(kāi)啟新的線(xiàn)程
- 只要是主隊(duì)列, 永遠(yuǎn)都在主線(xiàn)程中執(zhí)行
- 同步 + 主隊(duì)列 = 需要記住的就一點(diǎn): 同步函數(shù)不能搭配主隊(duì)列使用
- 注意: 有例外的情況, 如果同步函數(shù)是在異步函數(shù)中調(diào)用的, 那么沒(méi)有任何問(wèn)題
- GCD線(xiàn)程間通信示例
- 利用異步函數(shù)執(zhí)行任務(wù)
- 利用主隊(duì)列回到主線(xiàn)程更新UI
// 從子線(xiàn)程回到主線(xiàn)程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
//執(zhí)行耗時(shí)的異步操作...
dispatch_async(dispatch_get_main_queue(),^{
//回到主線(xiàn)程倒谷,執(zhí)行UI刷新操作
});
});
- GCD中的常用方法
- 延遲執(zhí)行
### 使用NSTimer
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
// 內(nèi)部實(shí)現(xiàn)原理就是NSTimer
### 調(diào)用NSObject的方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 將需要執(zhí)行的代碼, 和方法放在一起, 提高代碼的閱讀性
// 相比NSTimer來(lái)說(shuō), GCD的延遲執(zhí)行更加準(zhǔn)確
### 使用GCD函數(shù)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@", [NSThread currentThread]);
NSLog(@"run");
};
### run方法
-(void)run
{
NSLog(@"%s", __func__);
}
- 一次性代碼
- 整個(gè)程序運(yùn)行過(guò)程中, 只會(huì)執(zhí)行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"明哥寫(xiě)的代碼被執(zhí)行了");
});
- 快速迭代
/*
第一個(gè)參數(shù): 需要執(zhí)行幾次任務(wù)
第二個(gè)參數(shù): 隊(duì)列
第三個(gè)參數(shù): 當(dāng)前被執(zhí)行到得任務(wù)的索引
/*
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
NSLog(@"%@, %zd",[NSThread currentThread] , index);
});
### 通過(guò)比較for循環(huán)和apply遍歷時(shí)間,看看哪個(gè)比較高效
- (void)apply
{
/*
第一個(gè)參數(shù): 需要執(zhí)行幾次任務(wù)
第二個(gè)參數(shù): 隊(duì)列
第三個(gè)參數(shù): 當(dāng)前被執(zhí)行到得任務(wù)的索引
*/
/*
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
NSLog(@"%@, %zd",[NSThread currentThread] , index);
});
*/
// 1.獲取images文件夾中所有的文件
NSString *sourcePath = @"/Users/ming/Desktop/abc";
NSString *dest = @"/Users/ming/Desktop/lym";
// 2.獲取images文件夾中所有的文件
NSFileManager *mgr = [NSFileManager defaultManager];
NSArray *subPaths = [mgr subpathsAtPath:sourcePath];
// NSLog(@"%@", subPaths);
// 3.剪切文件到lym文件夾中
/*
CFAbsoluteTime begin = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < subPaths.count; i++) {
// 3.1獲取當(dāng)前遍歷到得文件的名稱(chēng)
NSString *fileNmae = subPaths[i];
// 3.2根據(jù)當(dāng)前文件的名稱(chēng), 拼接全路徑
NSString *fromPath = [sourcePath stringByAppendingPathComponent:fileNmae];
NSString *toPath = [dest stringByAppendingPathComponent:fileNmae];
NSLog(@"fromPath = %@", fromPath);
NSLog(@"toPath = %@", toPath);
[mgr moveItemAtPath:fromPath toPath:toPath error:nil];
}
CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
NSLog(@"花費(fèi)了%f秒", end -begin);
*/
CFAbsoluteTime begin = CFAbsoluteTimeGetCurrent();
dispatch_apply(subPaths.count, dispatch_get_global_queue(0, 0), ^(size_t index) {
// 3.1獲取當(dāng)前遍歷到得文件的名稱(chēng)
NSString *fileNmae = subPaths[index];
// 3.2根據(jù)當(dāng)前文件的名稱(chēng), 拼接全路徑
NSString *fromPath = [sourcePath stringByAppendingPathComponent:fileNmae];
NSString *toPath = [dest stringByAppendingPathComponent:fileNmae];
NSLog(@"fromPath = %@", fromPath);
NSLog(@"toPath = %@", toPath);
[mgr moveItemAtPath:fromPath toPath:toPath error:nil];
});
CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
NSLog(@"花費(fèi)了%f秒", end -begin); // 花費(fèi)了0.001280秒
}
- barrier
- 要想執(zhí)行完前面所有的任務(wù)再執(zhí)行barrier必須滿(mǎn)足兩個(gè)條件
所有任務(wù)都是在同一個(gè)隊(duì)列中
隊(duì)列不能是全局并行隊(duì)列, 必須是自己創(chuàng)建的隊(duì)列
barrier方法之前添加的任務(wù)會(huì)先被執(zhí)行, 只有等barrier方法之前添加的任務(wù)執(zhí)行完畢, 才會(huì)執(zhí)行barrier
而且如果是在barrier方法之后添加的任務(wù), 必須等barrier方法執(zhí)行完畢之后才會(huì)開(kāi)始執(zhí)行
-
隊(duì)列組(group)
- 如果想實(shí)現(xiàn), 等前面所有的任務(wù)都執(zhí)行完畢, 再執(zhí)行某一個(gè)特定的任務(wù), 那么可以通過(guò)GCD中的組來(lái)實(shí)現(xiàn)
- 只要當(dāng)前組中所有的任務(wù)都執(zhí)行完畢了, 那么系統(tǒng)會(huì)自動(dòng)調(diào)用dispatch_group_notify
實(shí)際開(kāi)發(fā): 有這么1種需求
- 首先:分別異步執(zhí)行2個(gè)耗時(shí)的操作
- 其次:等2個(gè)異步操作都執(zhí)行完畢后糙箍,再回到主線(xiàn)程執(zhí)行操作
- 如果想要快速高效地實(shí)現(xiàn)上述需求渤愁,可以考慮用隊(duì)列組/柵欄函數(shù)
- 詳細(xì)實(shí)現(xiàn)
使用group實(shí)現(xiàn)
- (void)group
{
// GCD組
// 1.創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", DISPATCH_QUEUE_CONCURRENT);
// 創(chuàng)建一個(gè)組
dispatch_group_t group = dispatch_group_create();
// 2.添加一個(gè)下載圖片任務(wù)
dispatch_group_async(group, queue, ^{
NSURL *url = [NSURL URLWithString:@"http://stimgcn1.s-msn.com/msnportal/ent/2015/08/04/7a59dbe7-3c18-4fae-bb56-305dab5e6951.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
self.image1 = [UIImage imageWithData:data];
NSLog(@"下載圖片1 %@", [NSThread currentThread]);
});
// 3.再添加一個(gè)下載圖片任務(wù)
dispatch_group_async(group, queue, ^{
NSURL *url = [NSURL URLWithString:@"http://y1.ifengimg.com/cmpp/2015/08/05/04/15495f65-5cd2-44cd-a704-bc455d629fe3_size25_w510_h339.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
self.image2 = [UIImage imageWithData:data];
NSLog(@"下載圖片2 %@", [NSThread currentThread]);
});
dispatch_group_notify(group, queue, ^{
NSLog(@"合成圖片 %@", [NSThread currentThread]);
// 1.創(chuàng)建圖片上下文
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
// 2.繪制第一張圖片
[self.image1 drawInRect:CGRectMake(0, 0, 100, 200)];
// 3.繪制第二張圖片
[self.image2 drawInRect:CGRectMake(100, 0, 100, 200)];
// 4.從上下文中取出圖片
UIImage *res = UIGraphicsGetImageFromCurrentImageContext();
// 5.關(guān)閉上下文
UIGraphicsEndImageContext();
// 6.回到主線(xiàn)程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"更新UI %@", [NSThread currentThread]);
self.imageView.image = res;
});
});
}
柵欄函數(shù)實(shí)現(xiàn)
- (void)barrier
{
// 如果所有的任務(wù)都在"同一個(gè)"隊(duì)列中
// 那么在barrier方法之前添加的任務(wù)會(huì)先被執(zhí)行, 只有等barrier方法之前添加的任務(wù)執(zhí)行完畢, 才會(huì)執(zhí)行barrier
// 而且如果是在barrier方法之后添加的任務(wù), 必須等barrier方法執(zhí)行完畢之后才會(huì)開(kāi)始執(zhí)行
// 1.創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", DISPATCH_QUEUE_CONCURRENT);
// dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2.添加一個(gè)下載圖片任務(wù)
dispatch_async(queue, ^{
NSURL *url = [NSURL URLWithString:@"http://stimgcn1.s-msn.com/msnportal/ent/2015/08/04/7a59dbe7-3c18-4fae-bb56-305dab5e6951.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
self.image1 = [UIImage imageWithData:data];
NSLog(@"下載圖片1 %@", [NSThread currentThread]);
});
// 3.再添加一個(gè)下載圖片任務(wù)
dispatch_async(queue, ^{
NSURL *url = [NSURL URLWithString:@"http://y1.ifengimg.com/cmpp/2015/08/05/04/15495f65-5cd2-44cd-a704-bc455d629fe3_size25_w510_h339.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
self.image2 = [UIImage imageWithData:data];
NSLog(@"下載圖片2 %@", [NSThread currentThread]);
});
// 要想執(zhí)行完前面所有的任務(wù)再執(zhí)行barrier必須滿(mǎn)足兩個(gè)條件
// 1. 所有任務(wù)都是在同一個(gè)隊(duì)列中
// 2. 隊(duì)列不能是全局并行隊(duì)列, 必須是自己創(chuàng)建的隊(duì)列
dispatch_barrier_async(queue, ^{
NSLog(@"合成圖片 %@", [NSThread currentThread]);
// 1.創(chuàng)建圖片上下文
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
// 2.繪制第一張圖片
[self.image1 drawInRect:CGRectMake(0, 0, 100, 200)];
// 3.繪制第二張圖片
[self.image2 drawInRect:CGRectMake(100, 0, 100, 200)];
// 4.從上下文中取出圖片
UIImage *res = UIGraphicsGetImageFromCurrentImageContext();
// 5.關(guān)閉上下文
UIGraphicsEndImageContext();
// 6.回到主線(xiàn)程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"更新UI %@", [NSThread currentThread]);
self.imageView.image = res;
});
});
}
4.NSOperation(掌握/理解)
- NSOperation的作用
- 配合使用NSOperation和NSOperationQueue也能實(shí)現(xiàn)多線(xiàn)程編程
- NSOperation和NSOperationQueue實(shí)現(xiàn)多線(xiàn)程的具體步驟:
- 1.先將需要執(zhí)行的操作封裝到一個(gè)NSOperation對(duì)象中
- 2.然后將NSOperation對(duì)象添加到NSOperationQueue中
- 3.系統(tǒng)會(huì)自動(dòng)將NSOperationQueue中的NSOperation取出來(lái)
- 4.將取出的NSOperation封裝的操作放到一條新線(xiàn)程中執(zhí)行
NSOperation的子類(lèi)
NSOperation是個(gè)抽象類(lèi),并不具備封裝操作的能力倍靡,必須使用它的子類(lèi)
-
使用NSOperation子類(lèi)的方式有3種
- NSInvocationOperation
- NSBlockOperation
- 自定義子類(lèi)繼承NSOperation猴伶,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法
NSInvocationOperation
創(chuàng)建NSInvocationOperation對(duì)象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
-
調(diào)用start方法開(kāi)始執(zhí)行操作
- (void)start;
- 一旦執(zhí)行操作,就會(huì)調(diào)用target的sel方法
-
注意
- 默認(rèn)情況下塌西,調(diào)用了start方法后并不會(huì)開(kāi)一條新線(xiàn)程去執(zhí)行操作他挎,而是在當(dāng)前線(xiàn)程同步執(zhí)行操作
- 只有將NSOperation放到一個(gè)NSOperationQueue中,才會(huì)異步執(zhí)行操作
NSBlockOperation
創(chuàng)建NSBlockOperation對(duì)象
+ (id)blockOperationWithBlock:(void (^)(void))block;
通過(guò)addExecutionBlock:方法添加更多的操作
- (void)addExecutionBlock:(void (^)(void))block;
注意:只要NSBlockOperation封裝的操作數(shù) >1捡需,就會(huì)異步執(zhí)行操作
NSOperationQueue
-
NSOperationQueue的作用
- NSOperation可以調(diào)用start方法來(lái)執(zhí)行任務(wù)办桨,但默認(rèn)是同步執(zhí)行的
- 如果將NSOperation添加到NSOperationQueue(操作隊(duì)列)中,系統(tǒng)會(huì)自動(dòng)異步執(zhí)行NSOperation中的操作
添加操作到NSOperationQueue中
-(void)addOperation:(NSOperation *)op;
-(void)addOperationWithBlock:(void (^)(void))block;
-
什么是并發(fā)數(shù)
- 同時(shí)執(zhí)行的任務(wù)數(shù)
- 比如站辉,同時(shí)開(kāi)3個(gè)線(xiàn)程執(zhí)行3個(gè)任務(wù)呢撞,并發(fā)數(shù)就是3
最大并發(fā)數(shù)的相關(guān)方法
-(NSInteger)maxConcurrentOperationCount;
-(void)setMaxConcurrentOperationCount:(NSInteger)cnt;
- 取消隊(duì)列的所有操作
-(void)cancelAllOperations;
提示:也可以調(diào)用NSOperation的- (void)cancel方法取消單個(gè)操作
- 暫停和恢復(fù)隊(duì)列
-(void)setSuspended:(BOOL)b;// YES代表暫停隊(duì)列,NO代表恢復(fù)隊(duì)列
-(BOOL)isSuspended;
-
操作依賴(lài)
-
NSOperation之間可以設(shè)置依賴(lài)來(lái)保證執(zhí)行順序
- 比如一定要讓操作A執(zhí)行完后饰剥,才能執(zhí)行操作B殊霞,可以這么寫(xiě)
[operationB addDependency:operationA]; // 操作B依賴(lài)于操作A
- 比如一定要讓操作A執(zhí)行完后饰剥,才能執(zhí)行操作B殊霞,可以這么寫(xiě)
-
可以在不同queue的NSOperation之間創(chuàng)建依賴(lài)關(guān)系
-
-
操作的監(jiān)聽(tīng)
- 可以監(jiān)聽(tīng)一個(gè)操作的執(zhí)行完畢
-(void (^)(void))completionBlock;
-(void)setCompletionBlock: (void (^)(void))block;
- 自定義NSOperation
-
自定義NSOperation的步驟很簡(jiǎn)單
- 重寫(xiě)
- (void)main
方法,在里面實(shí)現(xiàn)想執(zhí)行的任務(wù)
- 重寫(xiě)
-
重寫(xiě)-(void)main方法的注意點(diǎn)
- 自己創(chuàng)建自動(dòng)釋放池(因?yàn)槿绻钱惒讲僮魈兀瑹o(wú)法訪(fǎng)問(wèn)主線(xiàn)程的自動(dòng)釋放池)
- 經(jīng)常通過(guò)
- (BOOL)isCancelled
方法檢測(cè)操作是否被取消绷蹲,對(duì)取消做出響應(yīng)
-
NSOperation基本使用
//
// ViewController.m
// 了解-NSOperation基本使用
//
// Created by ming on 16/6/6.
// Copyright (c) 2015年 ming. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"%s", __func__);
//1. 封裝任務(wù)
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
// 主線(xiàn)程
NSLog(@"1---%@", [NSThread currentThread]);
}];
// 2.追加其它任務(wù)
// 注意: 在沒(méi)有隊(duì)列的情況下, 如果給BlockOperation追加其它任務(wù), 那么其它任務(wù)會(huì)在子線(xiàn)程中執(zhí)行
[op1 addExecutionBlock:^{
NSLog(@"2---%@", [NSThread currentThread]);
}];
[op1 addExecutionBlock:^{
NSLog(@"3---%@", [NSThread currentThread]);
}];
// 3.啟動(dòng)任務(wù)
[op1 start];
}
- (void)invocation
{
// 注意: 父類(lèi)不具備封裝操作的能力
// NSOperation *op = [[NSOperation alloc] init];
// 1.封裝任務(wù)
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
// 2.要想執(zhí)行任務(wù)必須調(diào)用start
[op1 start];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run2) object:nil];
[op2 start];
}
- (void)run
{
NSLog(@"%@", [NSThread currentThread]);
}
- (void)run2
{
NSLog(@"%@", [NSThread currentThread]);
}
@end
NSOpreatinoQueue的其它常用方法
//
// ViewController.m
// NSOpreatinoQueue的其它常用方法
//
// Created by apple on 16/6/6.
// Copyright (c) 2016年 Ming. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 1.創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
// 2.創(chuàng)建任務(wù)
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1-------%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2-------%@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3-------%@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"4-------%@", [NSThread currentThread]);
for (int i = 0; i < 1000; i++) {
NSLog(@"%i", i);
}
}];
NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"5-------%@", [NSThread currentThread]);
}];
// 3.添加依賴(lài)
[op5 addDependency:op1];
[op5 addDependency:op2];
[op5 addDependency:op3];
[op5 addDependency:op4];
// 4.監(jiān)聽(tīng)op4什么時(shí)候執(zhí)行完畢
op4.completionBlock = ^{
NSLog(@"op4中所有的操作都執(zhí)行完畢了");
};
// 4.添加任務(wù)到隊(duì)列
[queue addOperation:op1];
[queue addOperation:op2];
[queue2 addOperation:op3];
[queue2 addOperation:op4];
[queue addOperation:op5];
/*
// dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_queue_t queue = dispatch_queue_create("com.ming.lym", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("com.ming.lbj", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1-------%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-------%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-------%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue2, ^{
NSLog(@"4-------%@", [NSThread currentThread]);
});
*/
/*
dispatch_group_t group = dispatch_group_create();
dispatch_group_t group2 = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
NSLog(@"1-------%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"2-------%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"3-------%@", [NSThread currentThread]);
});
dispatch_group_notify(group2, queue, ^{
NSLog(@"4-------%@", [NSThread currentThread]);
});
*/
}
@end
-
自定義NSOperation下載圖片思路– 無(wú)沙盒緩存
自定義NSOperation下載圖片思路– 有沙盒緩存