一、基本知識
1. 進(jìn)程
- 系統(tǒng)中正在運(yùn)行的一個程序
- 每個進(jìn)程之間相互獨(dú)立「運(yùn)行在其專用且受保護(hù)的內(nèi)存空間內(nèi)」
- 進(jìn)程之間可以相互通訊
2. 線程
- 1 個進(jìn)程想執(zhí)行任務(wù),必須得有 1 條線程「每個進(jìn)程至少有一條線程」
- 1 個進(jìn)程所有的任務(wù)都在線程里執(zhí)行
- 1 個線程的任務(wù)的執(zhí)行都是串行「按順序多個任務(wù)孙咪,同一時間內(nèi) 1個線程只執(zhí)行 1個任務(wù)」
I. 線程的狀態(tài)
- 啟動線程
-(void)start;
- 阻塞線程
// 讓線程阻塞多久不做任務(wù)
+ (void)sleepUntilDate:(NSDate *)date;
// 示例 1:
[NSThread sleepUntilDate: [NSDate distanceFuture]]; //永遠(yuǎn)阻塞「當(dāng)前語句所在的線程」
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow:2]]; //阻塞 2秒「單位是秒,當(dāng)前語句所在的線程」
+ (void)sleepForTimeInterval:(NSTimeInterval) ti;
// 示例2:
[NSThread sleepForTimeInterval:2]; //阻塞 2秒「單位是秒粪摘,當(dāng)前語句所在線程」
- 強(qiáng)制停止線程
+(void)exit;
注意:一旦線程死亡了击喂,不能在開啟任務(wù),只能重新創(chuàng)建新的線程來完成任務(wù)
II. 線程的通信
定義:
- 1個線程傳遞數(shù)據(jù)給另 1個線程
- 在 1個線程中執(zhí)行完成特定任務(wù)后论笔,轉(zhuǎn)到 另一個線程 繼續(xù) 執(zhí)行任務(wù)
線程間的通信方法:
// 跳轉(zhuǎn)回 主線程 的 哪個方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
// 跳轉(zhuǎn)到 哪個線程的 哪個方法
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
// 利用 NSPort(端口對象) 通訊「了解」
// A線程操作 B線程采郎,需要 B將 1個Port對象 給A,A通過 Port對象 操作 B
3. 多線程「掌握」
定義:1個進(jìn)程 開啟多條線程狂魔,每條線程可以 并行「同時」執(zhí)行不同的任務(wù)
原理:
- 同一時間 CPU只處理 1條線程
- 多線程并發(fā)「同時」執(zhí)行蒜埋,其實(shí)是 CPU快速的在多條線程間調(diào)度「切換」
優(yōu)點(diǎn):適當(dāng)提高 程序執(zhí)行效率 和 資源利用率「CPU、內(nèi)存利用率」
缺點(diǎn):
- 創(chuàng)建線程有開銷「創(chuàng)建線程大約需要90毫秒的創(chuàng)建時間」
iOS的主要開銷- 內(nèi)核數(shù)據(jù)結(jié)構(gòu)「大約 1KB」
- 椬羁空間「子線程 521KB整份,主線程 1MB」
- 線程過多 會降低程序性能
- 線程過多 CPU在線程上的開銷越大
- 使用多線程,程序設(shè)計(jì)更加復(fù)雜「線程通信籽孙、多線程數(shù)據(jù)共享」
- 多個線程訪問同一個資源的時候會引發(fā) 數(shù)據(jù)錯亂 和 數(shù)據(jù)安全 問題
I. 主線程
定義:
- 程序運(yùn)行后烈评,默認(rèn)開啟 1條線程叫主線程「UI線程」
- 主要作用:顯示、刷新UI界面犯建、處理UI事件
注意:耗時操作放在主線程會卡住主線程讲冠,嚴(yán)重影響UI流暢度
II. 互斥鎖「使用線程同步技術(shù)」
線程同步 定義:多條線程在同一條線上執(zhí)行「按順序的執(zhí)行任務(wù)」
互斥鎖 格式:@synchronized ( 鎖對象 ){ 需要鎖定的代碼 }
注意:
- 使用前提:多線程共同搶奪同一個線程
- 鎖對象可以是任意對象「一般都是 self,即調(diào)用此方法的單例對象本身」
- 鎖定 1份代碼适瓦,多個線程共用 1個鎖對象「多個鎖對象無效」
優(yōu)點(diǎn):防止多線程搶奪資源造成的數(shù)據(jù)安全問題
缺點(diǎn):需要消耗大量的 CPU資源「一般不提倡使用」
III. 原子和非原子屬性
在 @property 的屬性中有
- 原子屬性
atomic
:為 setter方法加同步鎖「@property 屬性 默認(rèn)是 加鎖」
特點(diǎn):線程安全竿开,但需要消耗大量的資源 - 非原子屬性
nonatomic
:不加 同步鎖
特點(diǎn):非線程安全,適合內(nèi)存小的移動設(shè)備
注:加鎖玻熙、資源搶奪一般交給服務(wù)器處理否彩,減少客戶端壓力
二、iOS中多線程的實(shí)現(xiàn)方案
1. pthread「了解」
// 創(chuàng)建線程
#import<pthread.h>
pthread_t thread;
pthread_create(pthread_t *restrict, const pthread_attr_t *restrict, void *(*)(void *), void *restrict);
2. NSThread 「掌握」
I. 基本用法
-
+ (NSThread *)currentThread;
獲得當(dāng)前線程 -
+ (NSThread *)mainThread;
獲得主線程 -
- (BOOL)isMainThread;
查看 當(dāng)前方法所在的類 是否為主線程 -
+ (BOOL)isMainThread;
查看 當(dāng)前方法所在的方法 是否為主線程
II. 創(chuàng)建和啟動線程
- 1 個NSThread對象就是 1條線程
- 雖然這里的線程是局部變量揭芍,可是當(dāng)線程開啟后胳搞,線程會直到任務(wù)完成后銷毀
- 線程任務(wù)完成后會處于消亡狀態(tài),不能再次開啟称杨。只能重新創(chuàng)建新的線程在執(zhí)行任務(wù)肌毅。
// 方法1. 創(chuàng)建線程
NSThread *thread_1 = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"run方法的參數(shù),obj類型"];
// 以下這步可以省略
thread_1.name = @"這條線程的名字";
// 啟動線程「線程一啟動姑原,就會在線程thread中執(zhí)行 self的 run方法」
[thread start];
// 注:以下兩種方法 不返回線程對象
// 優(yōu)點(diǎn):簡單快捷
// 缺點(diǎn):無法對線程進(jìn)行詳細(xì)的設(shè)置
// 方法2. 創(chuàng)建后自動分離出線程悬而,并啟動
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"run方法的參數(shù), obj類型"];
// 方法3. 隱式創(chuàng)建,并啟動后臺線程
[self performSelectorInBackground:@selector(run) withObject:nil];
3. GCD「Grand Central Dispatch 宏大的中樞調(diào)度器」
優(yōu)勢
- 純 C 語言有許多強(qiáng)大的函數(shù)
- 進(jìn)行了 iOS 系統(tǒng)級的優(yōu)化
- 針對多核并行運(yùn)算提出的解決方案「會自動利用更多的CPU內(nèi)核」
- 自動管理線程的生命周期「創(chuàng)建線程锭汛、調(diào)度任務(wù)笨奠、銷毀線程」
使用步驟
- 創(chuàng)建隊(duì)列或使用系統(tǒng)提供的隊(duì)列
- 定制任務(wù)「線程操作」
- 將任務(wù)添加到 隊(duì)列「存放任務(wù)」中
1) 任務(wù)
任務(wù)的執(zhí)行方式「GCD會自動從隊(duì)列中取出任務(wù)袭蝗,放到對應(yīng)線程中執(zhí)行」
I. 同步任務(wù)「synchronize」
簡介
- 只能在當(dāng)前線程中執(zhí)行任務(wù),不能 擅自開啟新線程
- 注:當(dāng)前任務(wù)中 添加了同步函數(shù)般婆,先執(zhí)行完 同步函數(shù)到腥,后執(zhí)行 同步函數(shù)后的當(dāng)前任務(wù)操作
在當(dāng)前隊(duì)列添加 同步任務(wù),會阻塞當(dāng)前隊(duì)列
執(zhí)行步驟
- 阻塞當(dāng)前線程蔚袍,保持和當(dāng)前線程的同步
- 執(zhí)行同步任務(wù)中的任務(wù)
- 回到當(dāng)前隊(duì)列乡范,使當(dāng)前隊(duì)列不在阻塞,執(zhí)行當(dāng)前隊(duì)列的任務(wù)
示例結(jié)果:1啤咽、2晋辆、3
// block:需要執(zhí)行的任務(wù)
// dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
NSLog(@"1. %@", [NSThread currentThread]);
dispatch_queue_t newQueue = dispatch_queue_create("test.testName.queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(newQueue, ^{
NSLog(@"2. %@", [NSThread currentThread]);
});
NSLog(@"3. %@", [NSThread currentThread]);
II. 異步任務(wù)「asynchronous」
簡介
- 可以在新的線程執(zhí)行任務(wù),能 擅自開啟新線程
- 注:當(dāng)前任務(wù)中 添加異步函數(shù)宇整,先執(zhí)行完 當(dāng)前任務(wù)瓶佳,后 執(zhí)行異步函數(shù)
執(zhí)行步驟
- 執(zhí)行當(dāng)前線程「不會阻塞當(dāng)前線程」
- 執(zhí)行異步任務(wù)
示例結(jié)果:1、3鳞青、2
// dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
// 每個有block的函數(shù)都有對應(yīng)的 function函數(shù)
// dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work);
NSLog(@"1. %@", [NSThread currentThread]);
dispatch_queue_t newQueue = dispatch_queue_create("test.testName.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(newQueue, ^{
NSLog(@"2. %@", [NSThread currentThread]);
});
NSLog(@"3. %@", [NSThread currentThread]);
2)隊(duì)列
I. 串行隊(duì)列「Serial Dispatch Queue」
- 任務(wù)一個接著一個執(zhí)行
- 代表:**主線程隊(duì)列 **「不受 retain 和 release 的影響」
放到主隊(duì)列中的任務(wù)霸饲,會在主線程,UI界面的線程中執(zhí)行
// 1. 獲得主隊(duì)列
dispatch_queue_t qu = dispatch_get_main_queue();
// 2. 將任務(wù)加入隊(duì)列
// 2.1 主隊(duì)列+異步函數(shù):串行執(zhí)行盼玄,不能開啟新線程
dispatch_async(qu, ^{ /*任務(wù)*/ });
// 2.2 主隊(duì)列+同步函數(shù):串行執(zhí)行贴彼,不能開啟新線程「如果執(zhí)行的函數(shù)也在主隊(duì)列則,隊(duì)列阻塞埃儿,都無法執(zhí)行」
dispatch_sync(qu, ^{ /*任務(wù)*/ });
- 自己創(chuàng)建 1個串行隊(duì)列,隊(duì)列優(yōu)先級默認(rèn)為
DISPATCH_QUEUE_PRIORITY_DEFAULT
// 1. 創(chuàng)建 1個串行隊(duì)列
// label: 隊(duì)列名稱「格式: 作用.名稱.queue」融涣,隊(duì)列名稱會在 CrashLog 里標(biāo)明崩潰的隊(duì)列名稱
// dispatch_queue_attr_t: 隊(duì)列類型「并發(fā)/同步」
// dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
// 屬性:DISPATCH_QUEUE_SERIAL 等價(jià)于 NULL
dispatch_queue_t qu = dispatch_queue_create("down.Miao.queue", DISPATCH_QUEUE_SERIAL);
// 2. 將任務(wù)加入隊(duì)列
// 2.1 串行隊(duì)列+異步函數(shù):不能并發(fā)童番,可以開啟多線程
// 異步函數(shù):任務(wù)執(zhí)行完成后,不等待 任務(wù)執(zhí)行的隊(duì)列結(jié)束就返回得出的結(jié)果
dispatch_async(qu, ^{ /*任務(wù)*/ });
// 2.2 串行隊(duì)列+同步函數(shù):能并發(fā)威鹿,不會開啟新線程
// 同步函數(shù):任務(wù)執(zhí)行完成后剃斧,等待 任務(wù)執(zhí)行的隊(duì)列結(jié)束就返回得出的結(jié)果
dispatch_sync(qu, ^{ /*任務(wù)*/ });
// 3. 自己創(chuàng)建的隊(duì)列需要手動釋放「MRC模式」
dispatch_release(qu); // 當(dāng)然 也有 dispatch_retain() 方法
II. 并發(fā)隊(duì)列「Concurrent Dispatch Queue」
- 讓多個任務(wù) 并發(fā)(同時) 進(jìn)行「自動開啟多線程同時執(zhí)行任務(wù)」
并發(fā)只有在 異步函數(shù)dispatch_async
下有效 - 代表:全局并發(fā)隊(duì)列 「不受 retain 和 release 的影響」
- 所有應(yīng)用程序都能使用并發(fā)隊(duì)列
// 有 4 個執(zhí)行優(yōu)先級:
// - 高 :DISPATCH_QUEUE_PRIORITY_HIGH
// - 默認(rèn):DISPATCH_QUEUE_PRIORITY_DEFAULT
// - 低 :DISPATCH_QUEUE_PRIORITY_LOW
// - 后臺:DISPATCH_QUEUE_PRIORITY_BACKGROUND
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
// 不是自己創(chuàng)建的對象無法手動釋放
- 自己創(chuàng)建 1個并發(fā)隊(duì)列
// 1. 創(chuàng)建 1個并發(fā)隊(duì)列
dispatch_queue_t qu = dispatch_queue_create("com.Miao.queue", DISPATCH_QUEUE_CONCURRENT);
// 2. 將任務(wù)加入并發(fā)隊(duì)列
// 2.1 并行隊(duì)列+異步函數(shù):可以并發(fā),可以開啟新線程
dispatch_async(qu, ^{ /*任務(wù)*/ });
// 2.2 并行隊(duì)列+同步函數(shù):不能并發(fā)忽你,不會開啟新線程
dispatch_sync(qu, ^{ /*任務(wù)*/ });
III. 變更線程的優(yōu)先級
- 使用
dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);
多個串行隊(duì)列的優(yōu)先級都變更到同一隊(duì)列的優(yōu)先級幼东,會導(dǎo)致
原本互相可以并行的多個串行隊(duì)列,在目標(biāo)隊(duì)列上只能同時執(zhí)行一個處理
dispatch_set_target_queue(/*要設(shè)置優(yōu)先級的對列*/, /*與期望的優(yōu)先級相同的隊(duì)列*/);
IV. 線程通訊
示例:下載圖片
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 1. 耗時的異步操作
// 獲取圖片網(wǎng)絡(luò)路徑
NSURL *url = [NSURL URLWithString:@"圖片網(wǎng)絡(luò)路徑/圖片名.圖片格式"];
// 加載圖片
NSData *data = [NSData dataWithContentsOfURL: url];
// 生成圖片
UIImage *image = [UIImage imageWithData:data];
// 2. 回到主線程「最好用異步函數(shù)」
dispatch_async( dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
3)信號量
信號:多線程中的計(jì)數(shù)器科雳,計(jì)數(shù)為 0 時線程等待根蟹,計(jì)數(shù)為 1 或者大于 1 時,減去 1 而不等待
示例:由于信號量為 10 在這個并行隊(duì)列里糟秘,最多會有 10 個任務(wù)被執(zhí)行
// 創(chuàng)建信號量简逮,其值設(shè)為 10
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
for (int i = 0; i < 100; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 超時后,信號量會失效
// 信號量為 0尿赚,線程被阻塞散庶;信號量 不為 0蕉堰,信號總量 -1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"__%d__",i);
// 休眠
[NSThread sleepForTimeInterval:3];
// 信號量 +1
dispatch_semaphore_signal(semaphore);
});
}
4) GCD 其他方法
I. 障礙函數(shù)
使用限制
// 此方法阻塞的是 傳入的 queue「不是當(dāng)前線程」
// 參數(shù) queue 不能是 全局的并發(fā)隊(duì)列
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
執(zhí)行步驟
- 阻塞傳入的線程 queue
- queue 前面的任務(wù)執(zhí)行完畢,執(zhí)行 障礙函數(shù)
- 取消對線程 queue 的阻塞
dispatch_async(queue, ^{ /* 讀 */ });
dispatch_async(queue, ^{ /* 讀 */ });
dispatch_barrier_async(queue, ^{ /* 并行讀入完成悲龟,開始并行寫入 */ });
dispatch_async(queue, ^{ /* 寫 */ });
dispatch_async(queue, ^{ /* 寫 */ });
II. 延時函數(shù)「不影響現(xiàn)有的線程繼續(xù)執(zhí)行」
- NSObject 方法:
[self performSelector:@selector(函數(shù)1:) withObject:函數(shù)1的參數(shù) afterDelay: 2.0];
延遲 2秒 - NSTimer 方法:
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(函數(shù)1:) userInfo:nil repeats:NO];
- GCD 函數(shù)「非循環(huán)」:
dispatch_after( dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)),
// 2秒后到主線程執(zhí)行任務(wù)
dispatch_get_main_queue(), ^{
// 2秒后異步執(zhí)行這里的代碼...
});
- GCD 定時器「循環(huán)」:GCD 的定時器不受 RunLoop 的 Mode影響
int count = 0;
// 1. 獲得隊(duì)列
// dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_queue_t queue = dispatch_get_main_queue();
// 2. 創(chuàng)建一個定時器(dispatch_source_t本質(zhì)還是個OC對象)
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 3. 設(shè)置定時器的各種屬性(幾時開始任務(wù)屋讶,每隔多長時間執(zhí)行一次)
// GCD的時間參數(shù),一般是納秒(1秒 == 10的9次方納秒)
// 何時開始執(zhí)行第一個任務(wù)
// dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC) 比當(dāng)前時間晚3秒
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);
dispatch_source_set_timer(self.timer, start, interval, 0);
// 4. 設(shè)置回調(diào)
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"------------%@", [NSThread currentThread]);
count++;
if (count == 4) {
// 6. 取消定時器
dispatch_cancel(self.timer);
self.timer = nil;
}
});
// 5. 啟動定時器
dispatch_resume(self.timer);
III. 一次性代碼「保證代碼在程序運(yùn)行中须教,只執(zhí)行一次」
static dispatch_one_t onceToken; // 標(biāo)記是否執(zhí)行的過
dispatch_once(&onceToken, ^{ /*要只執(zhí)行一次的代碼塊丑婿,線程安全的已經(jīng)加鎖了*/ });
IV. 快讀迭代「可以多線程同時遍歷」
dispatch_queue_t qu = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 10為遍歷的數(shù)組長度
dispatch_apply(10, qu,^(size_t index){
// 遍歷需要的操作
});
VI. 隊(duì)列組
作用
- 首先,異步執(zhí)行多個耗時操作
- 其次没卸,等之前的操作全部完畢后羹奉,再回到主線程執(zhí)行操作
示例:
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個耗時的異步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行2個耗時的異步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后,回到主線程...
});
4. NSOperation「底層是GCD约计,做了面向?qū)ο蟮姆庋b」
作用
- 配合使用 NSOperation 和 NSOperationQueue 也能實(shí)現(xiàn)多線程編程
- NSOperation 是抽象類诀拭,并不具備封裝操作的能力,必須使用他的子類
1)NSOperation 的子類「了解」
I. NSInovcationOperation
注意:
- 默認(rèn)煤蚌,調(diào)用 start方法后 不會開啟一條新線程執(zhí)行耕挨,會在當(dāng)前線程同步執(zhí)行操作
- 只有將 NSOperation 放到 NSOperationQueue 里,才會執(zhí)行異步操作
// 創(chuàng)建NSInvocationOperation對象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
// 調(diào)用start方法開始執(zhí)行操作
// 一旦執(zhí)行操作尉桩,就會調(diào)用target的sel方法
- (void)start;
II. NSBlockOperation
注意:只要NSBlockOperation封裝的操作數(shù) > 1筒占,就會異步執(zhí)行操作
// 創(chuàng)建NSBlockOperation對象
// 默認(rèn)在當(dāng)前線程執(zhí)行
+ (id)blockOperationWithBlock:(void (^)(void))block;
// 通過addExecutionBlock:方法添加更多的操作
// 添加額外的任務(wù)會開啟新的線程異步執(zhí)行
- (void)addExecutionBlock:(void (^)(void))block;
III. 自定義類「繼承自NSOperation」
- 要實(shí)現(xiàn) NSOperation中的
-(void)main
將自定義類添加到 NSOperationQueue 中時會自動調(diào)用 實(shí)現(xiàn)的main函數(shù)
其中,要自己創(chuàng)建自動釋放池(因?yàn)槿绻钱惒讲僮髦├纾瑹o法訪問主線程的自動釋放池) -
-(BOOL)isConcurrent
是否能夠并發(fā)執(zhí)行 -
-(BOOL)isCancelled
檢測操作是否被取消翰苫,對取消做出響應(yīng)
還有很多等等
2)NSOperationQueue「掌握」
作用:將 NSOperation 添加到 NSOperationQueue 中,系統(tǒng)會自動異步執(zhí)行 NSOperation 中的操作
I. NSOperationQueue的隊(duì)列類型
主隊(duì)列
[NSOperationQueue mainQueue]
凡是添加到主隊(duì)列中的任務(wù)这橙,都會放到主線程中執(zhí)行非主隊(duì)列
[[NSOperationQueue alloc] init]
同時包含了:串行奏窑、并發(fā)功能
添加到這種隊(duì)列,就會自動放到子線程中執(zhí)行
// 1. 創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 設(shè)置最大并發(fā)數(shù)屈扎,如果為 1 則為串行
queue.maxConcurrentOperationCount = 1;
// 2. 創(chuàng)建操作(任務(wù))
// 2.1 創(chuàng)建NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
// 2.2 創(chuàng)建NSBlockOperation
// 快速創(chuàng)建并添加方式
[queue addOperationWithBlock:^{
NSLog(@"download5 --- %@", [NSThread currentThread]);
}];
// 普通方式
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
[op2 addExecutionBlock:^{
NSLog(@"download4 --- %@", [NSThread currentThread]);
}];
[op2 addExecutionBlock:^{
NSLog(@"download5 --- %@", [NSThread currentThread]);
}];
// 2.3 創(chuàng)建自定義任務(wù)
Person *op3 = [[Person alloc] init];
// 3. 添加任務(wù)到隊(duì)列中
[queue addOperation:op1]; // 不用調(diào)用start方法埃唯,自動開啟線程
[queue addOperation:op2];
[queue addOperation:op3];
II. NSOperationQueue 的方法
-
- (void)cancelAllOperations;
取消所有隊(duì)列的操作,會移除 所有隊(duì)列的操作 -
- (void)cancel;
取消單個隊(duì)列操作 -
@property (getter=isSuspended) BOOL suspended;
暫陀コ浚或恢復(fù)隊(duì)列墨叛,不會移除隊(duì)列
III. 線程的依賴和監(jiān)聽
- 依賴的作用:保證執(zhí)行順序
可以在 同一 / 不同隊(duì)列中 創(chuàng)建依賴關(guān)系
// 線程操作B 會在 線程操作A執(zhí)行完后 在執(zhí)行
[operationB addDependency:operationA]; // 操作B依賴于操作A
- 線程操作的監(jiān)聽
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
op2.completionBlock = ^{
NSLog(@"監(jiān)聽線程操作 op2執(zhí)行完畢后要執(zhí)行的操作");
};
[queue addOperation:op2];
IV. 線程間的通訊
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
// 圖片的網(wǎng)絡(luò)路徑
NSURL *url = [NSURL URLWithString:@"圖片路徑/圖片名.格式"];
// 加載圖片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成圖片
UIImage *image = [UIImage imageWithData:data];
// 回到主線程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
5. 多線程的應(yīng)用:單例設(shè)計(jì)模式「Singleton」
定義:
- 類的對象成為系統(tǒng)中唯一的實(shí)例,提供一個訪問點(diǎn)模蜡,供客戶類 共享資源
使用情景:「操作頻繁的時候漠趁,提升效率」
- 類只能有一個實(shí)例,必需從一個為人熟知的訪問點(diǎn)訪問哩牍,比如工廠方法
- 整個應(yīng)用程序中棚潦,共享一份資源
注意:
- 某個類只能有一個實(shí)例
- 為保證實(shí)例唯一性,這個方法必須是靜態(tài)類方法
- 單例方法名稱一般以 share 或 default 開頭
- 這個類必須自行創(chuàng)建這個對象
- 必須自行向整個系統(tǒng)提供這個實(shí)例
示例代碼一膝昆、使用GCD
// 實(shí)現(xiàn)之外丸边,先定義一個實(shí)例叠必,為了防止野指針,賦空值
// 實(shí)現(xiàn)之外的靜態(tài)變量妹窖,一個類只有一個「不能用繼承來實(shí)現(xiàn)不同類的單例」
static Sample *_instance = nil;
@implement Sample
// alloc纬朝、allocWithZone方法都會調(diào)用 allocWithZone方法骄呼,所以只重寫 allocWithZone方法
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
// 確保多線程的線程安全
static dispach_one_t onceToken;
dispach_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
// 直接返回單例對象
+ (instancetype)shareInstance{
// 確保多線程的線程安全
static dispatch_one_t onceToken;
dispach_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
// 防止 Copy方法的誤用
-(id)copyWithZone:(NSZone *)zone{
return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone{
return _instance;
}
// 若是MRC模式下共苛,需要添加一下代碼
- (oneway void)release{
// 為保證單例,只重寫父類方法蜓萄,什么都不需要做
}
- (instancetype)retain{
return _instance;
}
- (NSInteger)retainCount{
return MAXFLOAT; //為了方便程序員交流隅茎,這里返回一個很大的數(shù)
}
@end
示例代碼二、不使用GCD
static Sample _instance = nil;
@implement Sample
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
// 添加同步鎖嫉沽,注意加鎖的位置
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
+ (instancetype)sharedInstance {
// 添加同步鎖
@synchronized(self) {
if (_instance == nil) {
_instance = [[self alloc] init];
}
}
return _instance;
}
// 以下代碼與使用GCD時重復(fù)辟犀,略
@end
將單例作為宏定義使用
單例抽取為宏
優(yōu)點(diǎn):每次定義單例的方式都是一樣的,為了提高編程效率
缺點(diǎn):因?yàn)閱卫x中含有全局變量static
绸硕,在程序運(yùn)行的整個過程中只創(chuàng)建一份堂竟,所以在 使用宏時不能繼承單例宏方法定義
// 聲明方法,
// #define a(b) is_##b 的作用是 將 a(hehe) 自動替換為 is_hehe
#define interfaceSingleton(name) +(instancetype)share##name;
// 定義方法
// 系統(tǒng)自帶 宏定義 判斷是否為 arc模式
#if !__has_feature(obj_arc)
#define implememtationSingleton(name) \
static id _instance = nil;\
+ (instancetype)allocWithZone:(struct _NSZone *)zone{\
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
+ (instancetype)share##name{\
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{\
_instance = [[self alloc] init];\
});\
return _instance;\
}\
- (id)copyWithZone:(NSZone *)zone{ return _instance; }\
- (id)mutableCopyWithZone:(NSZone *)zone{ return _instance; }\
- (oneway void)release{}\
- (instancetype)retain{ return _instance; }\
- (NSInteger)retainCount{ return (unsigned long)MAXFLOAT}
#else
#define implememtationSingleton(name) \
static id _instance = nil;\
+ (instancetype)allocWithZone:(struct _NSZone *)zone{\
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
+ (instancetype)share##name{\
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{\
_instance = [[self alloc] init];\
});\
return _instance;\
}\
- (id)copyWithZone:(NSZone *)zone{ return _instance; }\
- (id)mutableCopyWithZone:(NSZone *)zone{ return _instance; }
#endif
- 宏方法使用
interfaceSingleton(className) // 這里沒有「;」因?yàn)槭呛甓x的使用
implementationSingleton(className)