總結(jié)一下多線程的東西,防止忘記卢鹦。。。不然每次都得翻出來重新看
基礎(chǔ)簡介
進(jìn)程
進(jìn)程是指在系統(tǒng)中正在運行的一個應(yīng)用程序
每個進(jìn)程之間是獨立的,每個進(jìn)程均運行在某專用且受保護(hù)的內(nèi)存空間內(nèi)
線程
1個進(jìn)程要想執(zhí)行任務(wù),必須得有線程存在(每一個進(jìn)程至少有一個線程)
1個進(jìn)程(程序)的所有任務(wù)都在線程里完成
線程的執(zhí)行是串行的,一個一個按順序執(zhí)行
所以一般優(yōu)化程序流暢度,就要開啟多個線程,多個線程之間是并行的,可以分開處理不同的操作,一起執(zhí)行
多線程的原理
同一時間,CPU只能處理1條線程,只有1條線程在工作(執(zhí)行)
多線程并發(fā)(同時)執(zhí)行,其實是CPU快速地在多線程之間調(diào)度(切換)
線程不能太多
如果線程過多,CPU調(diào)度不過來,會卡死
每條線程都會執(zhí)行緩慢
多線程的優(yōu)點
能適當(dāng)提高程序的執(zhí)行效率
能適當(dāng)?shù)奶岣哔Y源利用率(CPU,內(nèi)存的利用率,不會空轉(zhuǎn))
多線程的缺點
-
創(chuàng)建線程是有開銷的,iOS主要成本包括:
- 內(nèi)核數(shù)據(jù)結(jié)構(gòu)(大約1KB)
- 椄鸪空間(子線程512KB,主線程1MB,也可以使用-setStackSize:設(shè)置,但必須是4K的倍數(shù),而且最小是16K,不建議自己設(shè)定,系統(tǒng)既然設(shè)定了就是有用的),創(chuàng)建線程大約需要90毫秒的創(chuàng)建時間
- 線程開啟太多,會降低程序的性能,iOS一般最多5條線程
- 線程越多,CPU在調(diào)度線程上的開銷越大
- 程序設(shè)計更加復(fù)雜:比如線程之間的通信,多線程的數(shù)據(jù)共享
主線程
一個程序運行后,默認(rèn)會開啟一條線程,成為"主線程"或者"UI線程"
主線程的作用
顯示\刷新UI界面
處理UI時間(比如點擊事件\滾動事件\拖拽事件等)
主線程的使用注意
耗時操作放在子線程,不要放在主線程
四中多線程的實現(xiàn)
pthead
純C語言的,這玩意兒基本上不用
創(chuàng)建代碼
// 第一個傳一個
pthread_create(<#pthread_t *restrict#>, <#const pthread_attr_t *restrict#>, <#void *(*)(void *)#>, <#void *restrict#>)
第一個參數(shù)需要創(chuàng)建pthread_t對象地址,第三個參數(shù)傳一個C語言函數(shù),第二個和第四個參數(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;
}
這個C的東西,反正我沒用過...
NSThread
這種多線程的方法是比較輕量級,全程自己手動管理生命周期,同步,加鎖等問題,封裝了C語言的多線程
- 動態(tài)方法
/**
* selector : 線程執(zhí)行的方法,這個selector最多只能接收一個參數(shù)
* target : selector是消息發(fā)送的對象,發(fā)給誰的
* argument : 傳給selector的唯一參數(shù)
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;
// 例子,線程的開啟
// 初始化線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 設(shè)置線程的優(yōu)先級(0.0 ~ 1.0,1.0是最高級)
thread.threadPriority = 1;
// 設(shè)置線程的名字
thread.name = @"jack";
// 開啟線程
[thread start];
- 其他創(chuàng)建多線程的方法
- 靜態(tài)方法
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
// 例子
// 調(diào)用完畢后,會馬上創(chuàng)建并開啟新線程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
- 隱式創(chuàng)建線程
[self performSelectorInBackground:@selector(run) withObject:nil]
優(yōu)點: 簡單快捷
缺點:無法對線程進(jìn)行更詳細(xì)的設(shè)置
相關(guān)方法
+ (NSThread *)mainThread; // 獲得主線程
- (BOOL)isMainThread; // 是否為主線程
+ (BOOL)isMainThread; // 是否為主線程
獲得當(dāng)前線程
NSThread *current = [NSThread currentThread];
線程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;
控制線程的狀態(tài)
- 啟動線程
// 進(jìn)入就緒狀態(tài)->運行狀態(tài)-- 執(zhí)行完畢 -- > 死亡狀態(tài)
- (void)start;
- 阻塞(暫停)線程
- (void)sleepUntilDate:(NSDate *)date;
// 例子
// [NSThread sleepUntilDate:[NSDate distantFuture]]; // [NSDate distantFuture]從現(xiàn)在開始到永遠(yuǎn)
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
- (void)sleepForTimeInterval:(NSTimeInterval)ti;
[NSThread sleepForTimeInterval:2]; // 讓線程睡眠2秒(阻塞2秒)
- 強(qiáng)制死亡狀態(tài)
// 強(qiáng)制停止線程
+ (void)exit;
一旦線程死亡,就必須重新開啟新的線程,不能重新調(diào)用
線程鎖
在多個線程同時訪問一個對象的時候,同時對該對象進(jìn)行了操作,就會出現(xiàn)安全隱患,用NSThread的方法需要自己管理線程鎖,這就是互斥鎖
- 互斥鎖的使用格式
@synchronized(鎖對象) { // 需要鎖定的代碼 }
// 一份代碼只有一把鎖,多把鎖是無效的,還是亂的
// 這個鎖一般是全局變量,表示唯一的(切記)
互斥鎖的優(yōu)缺點
優(yōu)點:能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問題
缺點:需要消耗大量的CPU資源
加互斥鎖也就是要打到"線程同步"的問題
線程同步的意思是:多條線程在同一條線上執(zhí)行(按順序地執(zhí)行任務(wù))
互斥鎖嗦明,就是使用了線程同步技術(shù)
意思就是說所有線程上同一對象是一樣的!!!!
線程之間的通信
在一個進(jìn)程中,多個線程之間是有一定關(guān)聯(lián),相互通信的
- 一般在子線程進(jìn)行復(fù)雜操作,在主線程刷新UI,這就是線程通信
常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
// 最后這個(BOOL)wait是指下面代碼是否等這個執(zhí)行完了之后再進(jìn)行
- 蘋果還提供了第二種線程同步的方法NSMachPort
NSPort有3個子類:NSSocketPort诗良、NSMessagePort、NSMachPort督禽,但在iOS下只有NSMachPort可用。
使用的方式為接收線程中注冊NSMachPort,在另外的線程中使用此port發(fā)送消息菠红,則被注冊線程會收到相應(yīng)消息第岖,然后最終在主線程里調(diào)用某個回調(diào)函數(shù)。
可以看到,使用NSMachPort的結(jié)果為調(diào)用了其它線程的1個函數(shù)遇绞,而這正是performSelector所做的事情摹闽,所以舵匾,NSMachPort是個雞肋谎替。線程間通信應(yīng)該都通過performSelector來搞定。
GCD
Grand Central Dispatch(中央調(diào)度)
純C語言院喜,對NSThread進(jìn)行了封裝
切記:
不要在當(dāng)前穿行隊列里使用sync函數(shù)亡蓉,因為會卡住線程,調(diào)用sync函數(shù)硫麻,系統(tǒng)會馬上去執(zhí)行這個函數(shù)爸邢,但是當(dāng)前線程也是串行的,他會默認(rèn)執(zhí)行完當(dāng)前線程再去執(zhí)行別的拿愧,這就造成了沖突杠河,會卡死
GCD優(yōu)勢:
- 為多核的并發(fā)運算提出的解決方法
- 自動利用更多的CPU內(nèi)核(比如雙核,四核)
- 自動管理線程的生命周期(創(chuàng)建線程浇辜,調(diào)度任務(wù)券敌,銷毀線程等)
- 只需要告訴GCD執(zhí)行什么,GCD會自行管理任務(wù)
GCD兩個核心概念
- 任務(wù):執(zhí)行什么操作
- 隊列:用來存放任務(wù)
GCD的使用就2個步驟
- 定制任務(wù)
- 確定想做的事情
將任務(wù)添加到隊列中
- GCD會自動將隊列中的任務(wù)取出柳洋,放到對應(yīng)的線程中執(zhí)行
- 任務(wù)的取出遵循隊列的FIFO原則:先進(jìn)先出待诅,后進(jìn)后出
GCD的OC對象的內(nèi)存管理
GCD支持Cocoa內(nèi)存管理機(jī)制,因此可以在提交到queue的block中自由地使用Objective-C對象。每個dispatch queue維護(hù)自己的autorelease pool確保釋放autorelease對象,但是queue不保證這些對象實際釋放的時間熊镣。如果應(yīng)用消耗大量內(nèi)存,并且創(chuàng)建大量autorelease對象,你需要創(chuàng)建自己的autorelease pool,用來及時地釋放不再使用的對象卑雁。
同步和異步的區(qū)別
- 同步:只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟線程的能力
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
- 異步:可以在新的線程中執(zhí)行任務(wù)绪囱,具備開啟線程的能力
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
例子
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self syncConcurrent];
}
/**
* 同步函數(shù) + 主隊列:
*/
- (void)syncMain
{
NSLog(@"syncMain ----- begin");
// 1.獲得主隊列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.將任務(wù)加入隊列
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ù) + 主隊列:只在主線程中執(zhí)行任務(wù)
*/
- (void)asyncMain
{
// 1.獲得主隊列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.將任務(wù)加入隊列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 同步函數(shù) + 串行隊列:不會開啟新的線程测蹲,在當(dāng)前線程執(zhí)行任務(wù)。任務(wù)是串行的鬼吵,執(zhí)行完一個任務(wù)励幼,再執(zhí)行下一個任務(wù)
*/
// 倆參數(shù)判耕,第一個是名字鞭呕,第二個是執(zhí)行的優(yōu)先級
/**
* 全局并發(fā)隊列的優(yōu)先級
* #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 // 后臺
*/
- (void)syncSerial
{
// 1.創(chuàng)建串行隊列
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
// 2.將任務(wù)加入隊列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 異步函數(shù) + 串行隊列:會開啟新的線程吱瘩,但是任務(wù)是串行的荷逞,執(zhí)行完一個任務(wù)媒咳,再執(zhí)行下一個任務(wù)
*/
- (void)asyncSerial
{
// 1.創(chuàng)建串行隊列
// 兩個參數(shù),第一個是名字种远,第二個是穿行隊列
// DISPATCH_QUEUE_CONCURRENT 并行隊列
// DISPATCH_QUEUE_SERIAL 串行隊列
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
// 默認(rèn)是穿行隊列涩澡,所以第二個參數(shù)傳NULL也行
![Uploading Snip20160321_7_826971.png . . .]
// dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL);
// 2.將任務(wù)加入隊列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 同步函數(shù) + 并發(fā)隊列:不會開啟新的線程
* 因為還是同步函數(shù),所以你的并發(fā)執(zhí)行并沒有什么卵用
*/
- (void)syncConcurrent
{
// 1.獲得全局的并發(fā)隊列
// 在你程序啟動的時候坠敷,實際上GCD已經(jīng)給你創(chuàng)建了幾個全局的隊列妙同,你拿來用就好了射富,所以其實可以不用自己創(chuàng)建
// 倆參數(shù),第一個為線程的優(yōu)先級粥帚,第二個為預(yù)留參數(shù)胰耗,傳0就行了
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.將任務(wù)加入隊列
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ā)隊列:可以同時開啟多條線程
*/
- (void)asyncConcurrent
{
// 1.創(chuàng)建一個并發(fā)隊列
// label : 相當(dāng)于隊列的名字
// 倆參數(shù),第一個是線程的名字芒涡,第二個是優(yōu)先級
// dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
// 1.獲得全局的并發(fā)隊列
// 在你程序啟動的時候柴灯,實際上GCD已經(jīng)給你創(chuàng)建了幾個全局的隊列,你拿來用就好了费尽,所以其實可以不用自己創(chuàng)建
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.將任務(wù)加入隊列
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里面線程之間的通信
- (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];
// 回到主線程 (通信)
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
}
GCD的其他使用
- barrier障礙
把幾個線程分開赠群,先執(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里面查描,第一個參數(shù)為開始時間,第二個參數(shù)為延遲的秒數(shù)柏卤,單位為納米冬三,轉(zhuǎn)化單位NSEC_PER_SEC ,第三個參數(shù)為執(zhí)行的線程
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í)行代碼缘缚,這個代碼在這一整個進(jìn)程里就執(zhí)行一次长豁,不會執(zhí)行第二次
- (void)once
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"------run");
});
}
- 快速迭代(我感覺就是遍歷)
// 用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里面有個參數(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);
});
}
/**
* 對比:傳統(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ù)雜耗時操作放在子線程 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 剪切
[mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
});
}
}
- 設(shè)置依賴分組關(guān)系
- 創(chuàng)建隊列
- 創(chuàng)建group組對象dispatch_group_create()
- 講操作加到組里
- 在組里完成所有操作后,通過
dispatch_notify(<#object#>, <#queue#>, <#notification_block#>)
方法執(zhí)行下一步操作忙灼,有三個參數(shù)匠襟,第一個是哪一組的,第二個是在哪個隊列该园,第三個是要執(zhí)行的方法block
- (void)group
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 創(chuàng)建一個隊列組
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, ^{
// 開啟新的圖形上下文
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();
// 回到主線程顯示圖片
dispatch_async(dispatch_get_main_queue(), ^{
// 4.將新圖片顯示出來
self.imageView.image = image;
});
});
}
NSOperation
NSOperation的作用:配合使用NSOperation和NSOperationQueue也能實現(xiàn)多線程編程
NSOperation和NSOperationQueue實現(xiàn)多線程的具體步驟
- 先將需要執(zhí)行的操作封裝到一個NSOperation對象中
- 然后將NSOperation對象添加到NSOperationQueue中
- 系統(tǒng)會自動將NSOperationQueue中的NSOperation取出來
- 將取出的NSOperation封裝的操作放到一條新線程中執(zhí)行
NSOperation是個抽象類,并不具備封裝操作的能力里初,必須使用它的子類
使用NSOperation子類的方式有3種
- NSInvocationOperation(不常用)
- NSBlockOperation
- 自定義子類繼承NSOperation啃勉,實現(xiàn)內(nèi)部相應(yīng)的方法
NSInvocationOperation
- (void)invocationOperation
{
// 創(chuàng)建NSInvocationOperation對象
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
// 調(diào)用start方法開始執(zhí)行操作
[op start];
}
- 默認(rèn)情況下,調(diào)用start方法后并不會開一條新線程去執(zhí)行操作双妨,而是在當(dāng)前線程同步進(jìn)行操作
- 只有講NSOperation放到一個NSOperationQueue中淮阐,才會異步執(zhí)行操作
NSBlockOperation
在NSBlockOperation操作里,只要封裝的任務(wù)個數(shù)大于1刁品,就會異步執(zhí)行
- (void)blockOperation
{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 在主線程
NSLog(@"下載1------%@", [NSThread currentThread]);
}];
// 添加額外的任務(wù)(在子線程執(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方法的時候泣特,如果沒有加入NSOperationQueue,那么他們是同步在當(dāng)前線程執(zhí)行的挑随,只有加入了NSOperationQueue状您,系統(tǒng)會自動給你異步執(zhí)行,而且怎么異步,開多少線程都不用你管
兩種方式
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
示例如下
```objc
- (void)operationQueue1
{
// 創(chuàng)建隊列
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ù)到隊列中
[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)建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 創(chuàng)建操作
// NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
// NSLog(@"download1 --- %@", [NSThread currentThread]);
// }];
// 添加操作到隊列中
// [queue addOperation:op1];
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
}
- 最大并發(fā)數(shù)
可以設(shè)置最大并發(fā)數(shù)膏孟,也就是最多開啟的線程數(shù)
把最大并發(fā)數(shù)設(shè)置成1眯分,就變成了串行
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
- 取消隊列的所有操作
- (void)cancelAllOperations;```
提示:也可以調(diào)用NSOperation的- (void)cancel方法取消單個操作
- 暫停和恢復(fù)隊列
```objc
- (void)setSuspended:(BOOL)b; // YES代表暫停隊列,NO代表恢復(fù)隊列
- (BOOL)isSuspended;
- 線程之間的依賴
這個設(shè)置起來比著GCD要簡單的多柒桑,GCD可以通過barrier弊决,建組的方式,然而魁淳,NSOperation卻只需要一行代碼丢氢,而且還可以設(shè)置不同線程之間的依賴等
- (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è)置依賴
[op3 addDependency:op1];
[op3 addDependency:op2];
[op3 addDependency:op4];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
}
同樣是下載圖片的依賴關(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:^{
// 開啟新的圖形上下文
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();
// 回到主線程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
[combine addDependency:download1];
[combine addDependency:download2];
[queue addOperation:download1];
[queue addOperation:download2];
[queue addOperation:combine];
}
- 線程之間的通訊
- (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];
// 回到主線程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
}