總結(jié)一下多線(xiàn)程的東西,防止忘記。。。不然每次都得翻出來(lái)重新看
基礎(chǔ)簡(jiǎn)介
進(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)
線(xiàn)程
1個(gè)進(jìn)程要想執(zhí)行任務(wù),必須得有線(xiàn)程存在(每一個(gè)進(jìn)程至少有一個(gè)線(xiàn)程)
1個(gè)進(jìn)程(程序)的所有任務(wù)都在線(xiàn)程里完成

線(xiàn)程的執(zhí)行是串行的,一個(gè)一個(gè)按順序執(zhí)行
所以一般優(yōu)化程序流暢度,就要開(kāi)啟多個(gè)線(xiàn)程,多個(gè)線(xiàn)程之間是并行的,可以分開(kāi)處理不同的操作,一起執(zhí)行
多線(xiàn)程的原理
同一時(shí)間,CPU只能處理1條線(xiàn)程,只有1條線(xiàn)程在工作(執(zhí)行)
多線(xiàn)程并發(fā)(同時(shí))執(zhí)行,其實(shí)是CPU快速地在多線(xiàn)程之間調(diào)度(切換)
線(xiàn)程不能太多
如果線(xiàn)程過(guò)多,CPU調(diào)度不過(guò)來(lái),會(huì)卡死
每條線(xiàn)程都會(huì)執(zhí)行緩慢
多線(xiàn)程的優(yōu)點(diǎn)
能適當(dāng)提高程序的執(zhí)行效率
能適當(dāng)?shù)奶岣哔Y源利用率(CPU,內(nèi)存的利用率,不會(huì)空轉(zhuǎn))
多線(xiàn)程的缺點(diǎn)
-
創(chuàng)建線(xiàn)程是有開(kāi)銷(xiāo)的,iOS主要成本包括:
- 內(nèi)核數(shù)據(jù)結(jié)構(gòu)(大約1KB)
- ??臻g(子線(xiàn)程512KB,主線(xiàn)程1MB,也可以使用-setStackSize:設(shè)置,但必須是4K的倍數(shù),而且最小是16K,不建議自己設(shè)定,系統(tǒng)既然設(shè)定了就是有用的),創(chuàng)建線(xiàn)程大約需要90毫秒的創(chuàng)建時(shí)間
- 線(xiàn)程開(kāi)啟太多,會(huì)降低程序的性能,iOS一般最多5條線(xiàn)程
- 線(xiàn)程越多,CPU在調(diào)度線(xiàn)程上的開(kāi)銷(xiāo)越大
- 程序設(shè)計(jì)更加復(fù)雜:比如線(xiàn)程之間的通信,多線(xiàn)程的數(shù)據(jù)共享
主線(xiàn)程
一個(gè)程序運(yùn)行后,默認(rèn)會(huì)開(kāi)啟一條線(xiàn)程,成為"主線(xiàn)程"或者"UI線(xiàn)程"
主線(xiàn)程的作用
顯示\刷新UI界面
處理UI時(shí)間(比如點(diǎn)擊事件\滾動(dòng)事件\拖拽事件等)
主線(xiàn)程的使用注意
耗時(shí)操作放在子線(xiàn)程,不要放在主線(xiàn)程
四中多線(xiàn)程的實(shí)現(xiàn)

pthead
純C語(yǔ)言的,這玩意兒基本上不用
創(chuàng)建代碼
// 第一個(gè)傳一個(gè)
pthread_create(<#pthread_t *restrict#>, <#const pthread_attr_t *restrict#>, <#void *(*)(void *)#>, <#void *restrict#>)
第一個(gè)參數(shù)需要?jiǎng)?chuàng)建pthread_t對(duì)象地址,第三個(gè)參數(shù)傳一個(gè)C語(yǔ)言函數(shù),第二個(gè)和第四個(gè)參數(shù)傳NULL
// 例子
pthread_t thread2;
pthread_create(&thread2, NULL, buttonClick, NULL);
void * buttonClick(void *param)
{
for (NSInteger i = 0; i<10000; i++) {
NSLog(@"------buttonClick---%zd--%@", i, [NSThread currentThread]);
}
return NULL;
}
這個(gè)C的東西,反正我沒(méi)用過(guò)...
NSThread
這種多線(xiàn)程的方法是比較輕量級(jí),全程自己手動(dòng)管理生命周期,同步,加鎖等問(wèn)題,封裝了C語(yǔ)言的多線(xiàn)程
- 動(dòng)態(tài)方法
/**
* selector : 線(xiàn)程執(zhí)行的方法,這個(gè)selector最多只能接收一個(gè)參數(shù)
* target : selector是消息發(fā)送的對(duì)象,發(fā)給誰(shuí)的
* argument : 傳給selector的唯一參數(shù)
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;
// 例子,線(xiàn)程的開(kāi)啟
// 初始化線(xiàn)程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 設(shè)置線(xiàn)程的優(yōu)先級(jí)(0.0 ~ 1.0,1.0是最高級(jí))
thread.threadPriority = 1;
// 設(shè)置線(xiàn)程的名字
thread.name = @"jack";
// 開(kāi)啟線(xiàn)程
[thread start];
- 其他創(chuàng)建多線(xiàn)程的方法
- 靜態(tài)方法
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
// 例子
// 調(diào)用完畢后,會(huì)馬上創(chuàng)建并開(kāi)啟新線(xiàn)程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
- 隱式創(chuàng)建線(xiàn)程
[self performSelectorInBackground:@selector(run) withObject:nil]
優(yōu)點(diǎn): 簡(jiǎn)單快捷
缺點(diǎn):無(wú)法對(duì)線(xiàn)程進(jìn)行更詳細(xì)的設(shè)置
相關(guān)方法
+ (NSThread *)mainThread; // 獲得主線(xiàn)程
- (BOOL)isMainThread; // 是否為主線(xiàn)程
+ (BOOL)isMainThread; // 是否為主線(xiàn)程
獲得當(dāng)前線(xiàn)程
NSThread *current = [NSThread currentThread];
線(xiàn)程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;
控制線(xiàn)程的狀態(tài)
- 啟動(dòng)線(xiàn)程
// 進(jìn)入就緒狀態(tài)->運(yùn)行狀態(tài)-- 執(zhí)行完畢 -- > 死亡狀態(tài)
- (void)start;
- 阻塞(暫停)線(xiàn)程
- (void)sleepUntilDate:(NSDate *)date;
// 例子
// [NSThread sleepUntilDate:[NSDate distantFuture]]; // [NSDate distantFuture]從現(xiàn)在開(kāi)始到永遠(yuǎn)
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
- (void)sleepForTimeInterval:(NSTimeInterval)ti;
[NSThread sleepForTimeInterval:2]; // 讓線(xiàn)程睡眠2秒(阻塞2秒)
- 強(qiáng)制死亡狀態(tài)
// 強(qiáng)制停止線(xiàn)程
+ (void)exit;
一旦線(xiàn)程死亡,就必須重新開(kāi)啟新的線(xiàn)程,不能重新調(diào)用
線(xiàn)程鎖
在多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)一個(gè)對(duì)象的時(shí)候,同時(shí)對(duì)該對(duì)象進(jìn)行了操作,就會(huì)出現(xiàn)安全隱患,用NSThread的方法需要自己管理線(xiàn)程鎖,這就是互斥鎖
- 互斥鎖的使用格式
@synchronized(鎖對(duì)象) { // 需要鎖定的代碼 }
// 一份代碼只有一把鎖,多把鎖是無(wú)效的,還是亂的
// 這個(gè)鎖一般是全局變量,表示唯一的(切記)
互斥鎖的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):能有效防止因多線(xiàn)程搶奪資源造成的數(shù)據(jù)安全問(wèn)題
缺點(diǎn):需要消耗大量的CPU資源
加互斥鎖也就是要打到"線(xiàn)程同步"的問(wèn)題
線(xiàn)程同步的意思是:多條線(xiàn)程在同一條線(xiàn)上執(zhí)行(按順序地執(zhí)行任務(wù))
互斥鎖,就是使用了線(xiàn)程同步技術(shù)
意思就是說(shuō)所有線(xiàn)程上同一對(duì)象是一樣的!!!!
線(xiàn)程之間的通信
在一個(gè)進(jìn)程中,多個(gè)線(xiàn)程之間是有一定關(guān)聯(lián),相互通信的
- 一般在子線(xiàn)程進(jìn)行復(fù)雜操作,在主線(xiàn)程刷新UI,這就是線(xiàn)程通信

常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
// 最后這個(gè)(BOOL)wait是指下面代碼是否等這個(gè)執(zhí)行完了之后再進(jìn)行
- 蘋(píng)果還提供了第二種線(xiàn)程同步的方法NSMachPort
NSPort有3個(gè)子類(lèi):NSSocketPort、NSMessagePort、NSMachPort,但在iOS下只有NSMachPort可用。
使用的方式為接收線(xiàn)程中注冊(cè)NSMachPort,在另外的線(xiàn)程中使用此port發(fā)送消息,則被注冊(cè)線(xiàn)程會(huì)收到相應(yīng)消息,然后最終在主線(xiàn)程里調(diào)用某個(gè)回調(diào)函數(shù)。
可以看到,使用NSMachPort的結(jié)果為調(diào)用了其它線(xiàn)程的1個(gè)函數(shù),而這正是performSelector所做的事情,所以,NSMachPort是個(gè)雞肋。線(xiàn)程間通信應(yīng)該都通過(guò)performSelector來(lái)搞定。
GCD
Grand Central Dispatch(中央調(diào)度)
純C語(yǔ)言,對(duì)NSThread進(jìn)行了封裝
切記:
不要在當(dāng)前穿行隊(duì)列里使用sync函數(shù),因?yàn)闀?huì)卡住線(xiàn)程,調(diào)用sync函數(shù),系統(tǒng)會(huì)馬上去執(zhí)行這個(gè)函數(shù),但是當(dāng)前線(xiàn)程也是串行的,他會(huì)默認(rèn)執(zhí)行完當(dāng)前線(xiàn)程再去執(zhí)行別的,這就造成了沖突,會(huì)卡死
GCD優(yōu)勢(shì):
- 為多核的并發(fā)運(yùn)算提出的解決方法
- 自動(dòng)利用更多的CPU內(nèi)核(比如雙核,四核)
- 自動(dòng)管理線(xiàn)程的生命周期(創(chuàng)建線(xiàn)程,調(diào)度任務(wù),銷(xiāo)毀線(xiàn)程等)
- 只需要告訴GCD執(zhí)行什么,GCD會(huì)自行管理任務(wù)
GCD兩個(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)后出
GCD的OC對(duì)象的內(nèi)存管理
GCD支持Cocoa內(nèi)存管理機(jī)制,因此可以在提交到queue的block中自由地使用Objective-C對(duì)象。每個(gè)dispatch queue維護(hù)自己的autorelease pool確保釋放autorelease對(duì)象,但是queue不保證這些對(duì)象實(shí)際釋放的時(shí)間。如果應(yīng)用消耗大量?jī)?nèi)存,并且創(chuàng)建大量autorelease對(duì)象,你需要?jiǎng)?chuàng)建自己的autorelease pool,用來(lái)及時(shí)地釋放不再使用的對(duì)象。
同步和異步的區(qū)別
- 同步:只能在當(dāng)前線(xiàn)程中執(zhí)行任務(wù),不具備開(kāi)啟線(xiàn)程的能力
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
- 異步:可以在新的線(xiàn)程中執(zhí)行任務(wù),具備開(kāi)啟線(xiàn)程的能力
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

例子
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self syncConcurrent];
}
/**
* 同步函數(shù) + 主隊(duì)列:
*/
- (void)syncMain
{
NSLog(@"syncMain ----- begin");
// 1.獲得主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.將任務(wù)加入隊(duì)列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"syncMain ----- end");
}
/**
* 異步函數(shù) + 主隊(duì)列:只在主線(xiàn)程中執(zhí)行任務(wù)
*/
- (void)asyncMain
{
// 1.獲得主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.將任務(wù)加入隊(duì)列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 同步函數(shù) + 串行隊(duì)列:不會(huì)開(kāi)啟新的線(xiàn)程,在當(dāng)前線(xiàn)程執(zhí)行任務(wù)。任務(wù)是串行的,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
*/
// 倆參數(shù),第一個(gè)是名字,第二個(gè)是執(zhí)行的優(yōu)先級(jí)
/**
* 全局并發(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)
*/
- (void)syncSerial
{
// 1.創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
// 2.將任務(wù)加入隊(duì)列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 異步函數(shù) + 串行隊(duì)列:會(huì)開(kāi)啟新的線(xiàn)程,但是任務(wù)是串行的,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
*/
- (void)asyncSerial
{
// 1.創(chuàng)建串行隊(duì)列
// 兩個(gè)參數(shù),第一個(gè)是名字,第二個(gè)是穿行隊(duì)列
// DISPATCH_QUEUE_CONCURRENT 并行隊(duì)列
// DISPATCH_QUEUE_SERIAL 串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
// 默認(rèn)是穿行隊(duì)列,所以第二個(gè)參數(shù)傳NULL也行
![Uploading Snip20160321_7_826971.png . . .]
// dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL);
// 2.將任務(wù)加入隊(duì)列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 同步函數(shù) + 并發(fā)隊(duì)列:不會(huì)開(kāi)啟新的線(xiàn)程
* 因?yàn)檫€是同步函數(shù),所以你的并發(fā)執(zhí)行并沒(méi)有什么卵用
*/
- (void)syncConcurrent
{
// 1.獲得全局的并發(fā)隊(duì)列
// 在你程序啟動(dòng)的時(shí)候,實(shí)際上GCD已經(jīng)給你創(chuàng)建了幾個(gè)全局的隊(duì)列,你拿來(lái)用就好了,所以其實(shí)可以不用自己創(chuàng)建
// 倆參數(shù),第一個(gè)為線(xiàn)程的優(yōu)先級(jí),第二個(gè)為預(yù)留參數(shù),傳0就行了
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.將任務(wù)加入隊(duì)列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"syncConcurrent--------end");
}
/**
* 異步函數(shù) + 并發(fā)隊(duì)列:可以同時(shí)開(kāi)啟多條線(xiàn)程
*/
- (void)asyncConcurrent
{
// 1.創(chuàng)建一個(gè)并發(fā)隊(duì)列
// label : 相當(dāng)于隊(duì)列的名字
// 倆參數(shù),第一個(gè)是線(xiàn)程的名字,第二個(gè)是優(yōu)先級(jí)
// dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
// 1.獲得全局的并發(fā)隊(duì)列
// 在你程序啟動(dòng)的時(shí)候,實(shí)際上GCD已經(jīng)給你創(chuàng)建了幾個(gè)全局的隊(duì)列,你拿來(lái)用就好了,所以其實(shí)可以不用自己創(chuàng)建
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.將任務(wù)加入隊(duì)列
dispatch_async(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"1-----%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"2-----%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"3-----%@", [NSThread currentThread]);
}
});
NSLog(@"asyncConcurrent--------end");
// dispatch_release(queue);
}
GCD里面線(xiàn)程之間的通信
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 圖片的網(wǎng)絡(luò)路徑
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加載圖片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成圖片
UIImage *image = [UIImage imageWithData:data];
// 回到主線(xiàn)程 (通信)
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
}
GCD的其他使用
- barrier障礙
把幾個(gè)線(xiàn)程分開(kāi),先執(zhí)行障礙之前的代碼,然后執(zhí)行障礙,最后執(zhí)行障礙以后的代碼
dispatch_barrier_async(<#dispatch_queue_t queue#>, <#^(void)block#>)
例子
- (void)barrier
{
// dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
// 設(shè)置障礙
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
}
- 延遲執(zhí)行
dispatch_time延遲執(zhí)行的代碼在block里面,第一個(gè)參數(shù)為開(kāi)始時(shí)間,第二個(gè)參數(shù)為延遲的秒數(shù),單位為納米,轉(zhuǎn)化單位NSEC_PER_SEC ,第三個(gè)參數(shù)為執(zhí)行的線(xiàn)程
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
<#code to be executed after a specified delay#>
});
例子
- (void)delay
{
NSLog(@"touchesBegan-----");
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"run-----");
});
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
}
- (void)run
{
NSLog(@"run-----");
}
- 一次性代碼(單例要用)
// 一次執(zhí)行代碼,這個(gè)代碼在這一整個(gè)進(jìn)程里就執(zhí)行一次,不會(huì)執(zhí)行第二次
- (void)once
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"------run");
});
}
- 快速迭代(我感覺(jué)就是遍歷)
// 用GCD
- (void)apply
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSString *from = @"/Users/xiaomage/Desktop/From";
NSString *to = @"/Users/xiaomage/Desktop/To";
NSFileManager *mgr = [NSFileManager defaultManager];
NSArray *subpaths = [mgr subpathsAtPath:from];
// 快速迭代 block里面有個(gè)參數(shù)為size_t index
dispatch_apply(subpaths.count, queue, ^(size_t index) {
NSString *subpath = subpaths[index];
NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
// 剪切
[mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
NSLog(@"%@---%@", [NSThread currentThread], subpath);
});
}
/**
* 對(duì)比:傳統(tǒng)文件剪切
*/
- (void)moveFile
{
NSString *from = @"/Users/xiaomage/Desktop/From";
NSString *to = @"/Users/xiaomage/Desktop/To";
NSFileManager *mgr = [NSFileManager defaultManager];
NSArray *subpaths = [mgr subpathsAtPath:from];
for (NSString *subpath in subpaths) {
NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
// 復(fù)雜耗時(shí)操作放在子線(xiàn)程 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 剪切
[mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
});
}
}
- 設(shè)置依賴(lài)分組關(guān)系
- 創(chuàng)建隊(duì)列
- 創(chuàng)建group組對(duì)象dispatch_group_create()
- 講操作加到組里
- 在組里完成所有操作后,通過(guò)
dispatch_notify(<#object#>, <#queue#>, <#notification_block#>)方法執(zhí)行下一步操作,有三個(gè)參數(shù),第一個(gè)是哪一組的,第二個(gè)是在哪個(gè)隊(duì)列,第三個(gè)是要執(zhí)行的方法block
- (void)group
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 創(chuàng)建一個(gè)隊(duì)列組
dispatch_group_t group = dispatch_group_create();
// 1.下載圖片1
dispatch_group_async(group, queue, ^{
// 圖片的網(wǎng)絡(luò)路徑
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加載圖片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成圖片
self.image1 = [UIImage imageWithData:data];
});
// 2.下載圖片2
dispatch_group_async(group, queue, ^{
// 圖片的網(wǎng)絡(luò)路徑
NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
// 加載圖片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成圖片
self.image2 = [UIImage imageWithData:data];
});
// 3.將圖片1、圖片2合成一張新的圖片
dispatch_group_notify(group, queue, ^{
// 開(kāi)啟新的圖形上下文
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
// 繪制圖片
[self.image1 drawInRect:CGRectMake(0, 0, 50, 100)];
[self.image2 drawInRect:CGRectMake(50, 0, 50, 100)];
// 取得上下文中的圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 結(jié)束上下文
UIGraphicsEndImageContext();
// 回到主線(xiàn)程顯示圖片
dispatch_async(dispatch_get_main_queue(), ^{
// 4.將新圖片顯示出來(lái)
self.imageView.image = image;
});
});
}
NSOperation
NSOperation的作用:配合使用NSOperation和NSOperationQueue也能實(shí)現(xiàn)多線(xiàn)程編程
NSOperation和NSOperationQueue實(shí)現(xiàn)多線(xiàn)程的具體步驟
- 先將需要執(zhí)行的操作封裝到一個(gè)NSOperation對(duì)象中
- 然后將NSOperation對(duì)象添加到NSOperationQueue中
- 系統(tǒng)會(huì)自動(dòng)將NSOperationQueue中的NSOperation取出來(lái)
- 將取出的NSOperation封裝的操作放到一條新線(xiàn)程中執(zhí)行
NSOperation是個(gè)抽象類(lèi),并不具備封裝操作的能力,必須使用它的子類(lèi)
使用NSOperation子類(lèi)的方式有3種
- NSInvocationOperation(不常用)
- NSBlockOperation
- 自定義子類(lèi)繼承NSOperation,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法
NSInvocationOperation
- (void)invocationOperation
{
// 創(chuàng)建NSInvocationOperation對(duì)象
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
// 調(diào)用start方法開(kāi)始執(zhí)行操作
[op start];
}
- 默認(rèn)情況下,調(diào)用start方法后并不會(huì)開(kāi)一條新線(xiàn)程去執(zhí)行操作,而是在當(dāng)前線(xiàn)程同步進(jìn)行操作
- 只有講NSOperation放到一個(gè)NSOperationQueue中,才會(huì)異步執(zhí)行操作
NSBlockOperation
在NSBlockOperation操作里,只要封裝的任務(wù)個(gè)數(shù)大于1,就會(huì)異步執(zhí)行
- (void)blockOperation
{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 在主線(xiàn)程
NSLog(@"下載1------%@", [NSThread currentThread]);
}];
// 添加額外的任務(wù)(在子線(xiàn)程執(zhí)行)
[op addExecutionBlock:^{
NSLog(@"下載2------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下載3------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下載4------%@", [NSThread currentThread]);
}];
[op start];
}
NSOperationQueue
NSOperation一般是和NSOperationQueue配合使用的
在NSOperation調(diào)用start方法的時(shí)候,如果沒(méi)有加入NSOperationQueue,那么他們是同步在當(dāng)前線(xiàn)程執(zhí)行的,只有加入了NSOperationQueue,系統(tǒng)會(huì)自動(dòng)給你異步執(zhí)行,而且怎么異步,開(kāi)多少線(xiàn)程都不用你管
兩種方式
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
示例如下
```objc
- (void)operationQueue1
{
// 創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 創(chuàng)建操作(任務(wù))
// 創(chuàng)建NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
// 創(chuàng)建NSBlockOperation
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download3 --- %@", [NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"download4 --- %@", [NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"download5 --- %@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download6 --- %@", [NSThread currentThread]);
}];
// 創(chuàng)建XMGOperation
XMGOperation *op5 = [[XMGOperation alloc] init];
// 添加任務(wù)到隊(duì)列中
[queue addOperation:op1]; // [op1 start]
[queue addOperation:op2]; // [op2 start]
[queue addOperation:op3]; // [op3 start]
[queue addOperation:op4]; // [op4 start]
[queue addOperation:op5]; // [op5 start]
}
- (void)operationQueue2
{
// 創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 創(chuàng)建操作
// NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
// NSLog(@"download1 --- %@", [NSThread currentThread]);
// }];
// 添加操作到隊(duì)列中
// [queue addOperation:op1];
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
}
- 最大并發(fā)數(shù)
可以設(shè)置最大并發(fā)數(shù),也就是最多開(kāi)啟的線(xiàn)程數(shù)
把最大并發(fā)數(shù)設(shè)置成1,就變成了串行
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
- 取消隊(duì)列的所有操作
- (void)cancelAllOperations;```
提示:也可以調(diào)用NSOperation的- (void)cancel方法取消單個(gè)操作
- 暫停和恢復(fù)隊(duì)列
```objc
- (void)setSuspended:(BOOL)b; // YES代表暫停隊(duì)列,NO代表恢復(fù)隊(duì)列
- (BOOL)isSuspended;
- 線(xiàn)程之間的依賴(lài)
這個(gè)設(shè)置起來(lái)比著GCD要簡(jiǎn)單的多,GCD可以通過(guò)barrier,建組的方式,然而,NSOperation卻只需要一行代碼,而且還可以設(shè)置不同線(xiàn)程之間的依賴(lài)等
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download1----%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download2----%@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download3----%@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"download4----%@", [NSThread currentThread]);
}
}];
NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download5----%@", [NSThread currentThread]);
}];
op5.completionBlock = ^{
NSLog(@"op5執(zhí)行完畢---%@", [NSThread currentThread]);
};
// 設(shè)置依賴(lài)
[op3 addDependency:op1];
[op3 addDependency:op2];
[op3 addDependency:op4];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
}
同樣是下載圖片的依賴(lài)關(guān)系
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 想讓block里面改外面的局部變量的值,在外部參數(shù)加__block
__block UIImage *image1 = nil;
// 下載圖片1
NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
// 圖片的網(wǎng)絡(luò)路徑
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加載圖片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成圖片
image1 = [UIImage imageWithData:data];
}];
__block UIImage *image2 = nil;
// 下載圖片2
NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
// 圖片的網(wǎng)絡(luò)路徑
NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
// 加載圖片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成圖片
image2 = [UIImage imageWithData:data];
}];
// 合成圖片
NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
// 開(kāi)啟新的圖形上下文
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
// 繪制圖片
[image1 drawInRect:CGRectMake(0, 0, 50, 100)];
image1 = nil;
[image2 drawInRect:CGRectMake(50, 0, 50, 100)];
image2 = nil;
// 取得上下文中的圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 結(jié)束上下文
UIGraphicsEndImageContext();
// 回到主線(xiàn)程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
[combine addDependency:download1];
[combine addDependency:download2];
[queue addOperation:download1];
[queue addOperation:download2];
[queue addOperation:combine];
}
- 線(xiàn)程之間的通訊
- (void)test
{
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
// 圖片的網(wǎng)絡(luò)路徑
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加載圖片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成圖片
UIImage *image = [UIImage imageWithData:data];
// 回到主線(xiàn)程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
}