/*------------------------------ GCD使用 1.隊列和任務------------------------------------------*/程序員
重點:1."串行隊列"? "併發隊列"? 2.block?面試
{設計模式
1.GCD(Grand Central Dispatch) ---- '牛逼的中樞調度器'!安全
// C語言框架 / 自動管理線程的生命週期(建立/釋放)網絡
推出GCD的目的:取代NSThread!併發
爲"多核"的"並行"運算提出的解決方案!框架
優勢:異步
<1> GCD 可以自動利用更多的CPU的核數(雙核/四核)!async
<2> GCD 會自動管理線程的生命週期.函數
程序員只須要告訴 GCD 想要執行的任務(代碼)!
2.GCD中的兩個核心概念:
"任務":
想要作的事情/執行什麼操做.
GCD 中的任務定義在block中.
void (^myBlock)() = ^{
// 想要作的事情/任務
}
"隊列":
用來'存放'任務!
隊列 != 線程!
隊列中存放的任務最後都要由線程來執行!
隊列的原則:先進先出,後進後出(FIFO/ First In First Out)!
隊列的類型:
<1> '串行'隊列:(Serial Dispatch Queue)
存放按順序執行的任務!(一個任務執行完畢,再執行下一個任務)!
// 建立一個串行隊列
dispatch_queue_t serialQueue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
<2> '併發'隊列:(Concurrent Dispatch Queue)
存放想要同時(併發)執行的任務!
// 建立一個併發隊列
dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrent",DISPATCH_QUEUE_CONCURRENT);
注意兩個很是經常使用的特殊隊列:
<1> 主隊列: // UI 操做放在主隊列中執行!
跟主線程相關聯的隊列!
主隊列是 GCD 自帶的一種特殊的串行隊列!
主隊列中的任務都會在主線程中執行!
//獲取主隊列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
<2> 全局併發隊列: // 通常狀況下,併發任務均可以放在全局併發隊列中!
//獲取全局併發隊列
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
}
/*----------------------------------- GCD使用 2.執行任務 -------------------------------------*/
重點:1."同步"函數!"異步"函數! 2.容易混淆的四個概念: '串行' ,'併發' ,"同步" ,"異步"之間的區別?
{
問題:串行隊列中的任務一定按順序執行嗎?併發隊列中的任務一定同時執行嗎?
GCD中有兩個用來執行任務的函數:
'同步'執行任務:
dispatch_sync(<#dispatch_queue_t queue#>, <#^(void)block#>)
'異步'執行任務:
dispatch_async(dispatch_queue_t queue, <#^(void)block#>)
// <#dispatch_queue_t queue#> :隊列
// <#^(void)block#>:任務
"同步"和"異步"的區別:
"同步": 只能在'當前'線程中執行任務,不具有開啓新線程的能力.
"異步": 能夠在'新'的線程中執行任務,具有開啓新線程的能力.
GCD 使用有兩個步驟:
<1> 將任務添加到隊列中;
<2> 選擇同步仍是異步的方式執行任務.
注意:四個容易混淆的術語:
'串行' ,'併發' ,"同步" ,"異步".
}
/*------------------------------- GCD使用 3.各類隊列的執行效果 ---------------------------------*/
重點:1.掌握兩個經常使用的組合!
{
常見的組合(掌握)
1> dispatch_async + 全局併發隊列 (能夠開啓多條線程)
2> dispatch_async + 本身建立的串行隊列 (開啓一條線程)
只有'異步'執行"併發"隊列,才能夠開啓多條線程.
注意:
在主線程中同步執行主隊列中的任務,會形成'主線程'和'主隊列'相互等待,卡住主線程!
}
/*------------------------------- GCD使用 4.線程間通訊 ---------------------------------------*/
重點:1.從子線程回到主線程(經典用法)! 2.兩個注意點.
{
1.經典用法(子線程下載(耗時操做),主線程刷新UI):
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 執行耗時的異步操做...
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主線程,執行UI刷新操做
});
});
2.注意:
<1> 須要設置按鈕的image,建議先把按鈕類型改成custom,才能保證設置成功
<2> 屬性名不能以new開頭
}
/*------------------------------- GCD使用 5.延時執行 -----------------------------------------*/
重點:1.iOS常見的兩種延時執行方式
{
iOS中的延時執行方式:
// 定製好延時任務後,不會阻塞當前線程.
<1> 調用 NSObject 方法:
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 2秒後再調用self的run方法
<2> GCD 函數實現延時執行:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒後執行這裏的代碼... 在哪一個線程執行,跟隊列類型有關
});
注意:
不要使用sleep,會阻塞當前線程.
}
/*------------------------------- GCD使用 6.隊列組 ------------------------------------------*/
重點:1.瞭解隊列組的使用方法.
{
項目需求:
首先:分別異步執行兩個耗時操做;
其次:等兩次耗時操做都執行完畢後,再回到主線程執行操做.
使用隊列組(dispatch_group_t)快速,高效的實現上述需求.
dispatch_group_t group = dispatch_group_create(); // 隊列組
dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 全局併發隊列
dispatch_group_async(group, queue, ^{ // 異步執行操做1
// longTime1
});
dispatch_group_async(group, queue, ^{ // 異步執行操做2
// longTime2
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 在主線程刷新數據
// reload Data
});
}
/*------------------------------- GCD使用 7.一次性代碼 ---------------------------------------*/
重點:1.掌握一次性代碼的實現.
{
一次性代碼:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執行一次的代碼(這裏面默認是線程安全的).
});
}
/*-------------------------------------- 補充: 單例設計模式 -----------------------------------*/
重點:1.掌握單例!
{
1.單例簡介:
做用:
保證程序在運行過程當中,一個類只有一個實例對象.這個實例對象容易被外界訪問!
控制實例對象個數(只有一個),節約系統資源.
使用場合:
在整個應用程序中,共享一份資源(這份資源只須要建立初始化一次).
舉例:
打印機/視圖窗口/一些網絡工具類等等
// 懶漢式: 用到的時候再加載.
// 餓漢式: 只要程序運行就加載. // 不須要掌握,也不要這麼寫!
// 掌握懶漢式.
2.單例實現:(兩種方式:互斥鎖(@synchronized(self))和一次性代碼(dispatch_once));
2.1互斥鎖 @synchronized(self):
<1>.在 .m 文件中保留一個全局的 static 的實例.
static id _instance;
<2>.重寫若干方法(allocWithZone:和 copyWithZone:)並提供一個類方法讓外界訪問惟一的實例.
//(1)重寫 allocWithZone:方法,在這裏建立惟一的實例(注意線程安全). //alloc 內部都會調用這個方法.
+(instancetype)allocWithZone:(struct _NSZone *)zone {
if (_instance == nil) { // 防止頻繁加鎖
@synchronized(self) {
if (_instance == nil) { // 防止建立屢次
_instance = [super allocWithZone:zone];
}
}
}
return _instance;
}
//(2)重寫 copyWithZone:方法.
+(id)copyWithZone:(struct _NSZone *)zone
{
return _instance;
}
//(3)提供1個類方法讓外界訪問惟一的實例
+(instancetype)shareSingleton
{
if (!_instance) { // 防止頻繁加鎖
@synchronized(self){
if (!_instance) { // 防止建立屢次
_instance = [[self alloc] init];
}
}
}
return _instance;
}
2.2 一次性代碼(dispatch_once):
<1>.在 .m 文件中保留一個全局的 static 的實例.
static id _instance;
<2>.重寫若干方法(allocWithZone:和 copyWithZone:)並提供一個類方法讓外界訪問惟一的實例.
//(1)重寫 allocWithZone:方法,在這裏建立惟一的實例(注意線程安全).
+ (id)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instace = [super allocWithZone:zone];
});
return _instace;
}
//(2)重寫 copyWithZone:方法.
+(id)copyWithZone:(struct _NSZone *)zone
{
return _instance;
}
//(3)提供1個類方法讓外界訪問惟一的實例
+ (instancetype)shareSingleton
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instace = [[self alloc] init];
});
return _instace;
}
注意:在 ARC 和 MRC 中單例的實現方式略有不一樣. MRC 下單例的實現比 ARC 多了幾個內存管理的方法:
MRC 中增長以下方法的實現:
- (instancetype)retain { return self; }
- (NSUInteger)retainCount { return 1; }
- (oneway void)release {}
- (instancetype)autorelease { return self; }
3.判斷當前環境(ARC/MRC)
#if __has_feature(objc_arc)
// ARC
#else
// MRC
#endif
4.注意兩個方法:
// 面試問題:兩個方法的區別?
<1> +(void)load;
// 當類加載到OC運行時環境(內存)中的時候,就會調用一次(一個類只會加載一次).
// 程序一啓動就會調用.
// 程序運行過程當中,只會調用1次.
<2> +(void)initialize;
// 當第一次使用這個類的時候(好比調用了類的某個方法)纔會調用.
// 並不是程序一啓動就會調用.
}