補(bǔ)充
使用Crearte函數(shù)創(chuàng)建的并發(fā)隊(duì)列和全局并發(fā)隊(duì)列的主要區(qū)別:
1.全局并發(fā)隊(duì)列在整個(gè)應(yīng)用程序中本身是默認(rèn)存在的,并且對(duì)應(yīng)有高優(yōu)先級(jí)垮庐、默認(rèn)優(yōu)先級(jí)银觅、低優(yōu)先級(jí)和后臺(tái)優(yōu)先級(jí)一共四個(gè)并發(fā)隊(duì)列,我們只是選擇其中的一個(gè)直接拿來(lái)用践宴。而Crearte函數(shù)是實(shí)打?qū)嵉膹念^開始去創(chuàng)建一個(gè)隊(duì)列鲸匿。
2.在iOS6.0之前,在GCD中凡是使用了帶Create和retain的函數(shù)在最后都需要做一次release操作浴井。而主隊(duì)列和全局并發(fā)隊(duì)列不需要我們手動(dòng)release晒骇。當(dāng)然了,在iOS6.0之后GCD已經(jīng)被納入到了ARC的內(nèi)存管理范疇中,即便是使用retain或者create函數(shù)創(chuàng)建的對(duì)象也不再需要開發(fā)人員手動(dòng)釋放洪囤,我們像對(duì)待普通OC對(duì)象一樣對(duì)待GCD就OK徒坡。
3.在使用柵欄函數(shù)的時(shí)候,蘋果官方明確規(guī)定柵欄函數(shù)只有在和使用create函數(shù)自己的創(chuàng)建的并發(fā)隊(duì)列一起使用的時(shí)候才有效(沒(méi)有給出具體原因)
4.其它區(qū)別涉及到XNU內(nèi)核的系統(tǒng)級(jí)線程編程瘤缩,不一一列舉喇完。
5.給出一些參考資料(可以自行研究):
GCDAPI:https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/index.html#//apple_ref/c/func/dispatch_queue_create
Libdispatch版本源碼:http://www.opensource.apple.com/source/libdispatch/libdispatch-187.5/
1.單例模式
- 1.1 概念相關(guān)
(1)單例模式
在程序運(yùn)行過(guò)程,一個(gè)類只有一個(gè)實(shí)例
(2)使用場(chǎng)合
在整個(gè)應(yīng)用程序中剥啤,共享一份資源(這份資源只需要?jiǎng)?chuàng)建初始化1次)
- 1.2 ARC實(shí)現(xiàn)單例
(1)步驟
01 在類的內(nèi)部提供一個(gè)static修飾的全局變量
02 提供一個(gè)類方法锦溪,方便外界訪問(wèn)
03 重寫+allocWithZone方法,保證永遠(yuǎn)都只為單例對(duì)象分配一次內(nèi)存空間
04 嚴(yán)謹(jǐn)起見(jiàn)府怯,重寫-copyWithZone方法和-MutableCopyWithZone方法
(2)相關(guān)代碼
//提供一個(gè)static修飾的全局變量刻诊,強(qiáng)引用著已經(jīng)實(shí)例化的單例對(duì)象實(shí)例
static XMGTools *_instance;
//類方法,返回一個(gè)單例對(duì)象
+(instancetype)shareTools
{
//注意:這里建議使用self,而不是直接使用類名Tools(考慮繼承)
return [[self alloc]init];
}
//保證永遠(yuǎn)只分配一次存儲(chǔ)空間
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
//使用GCD中的一次性代碼
// static dispatch_once_t onceToken;
// dispatch_once(&onceToken, ^{
// _instance = [super allocWithZone:zone];
// });
//使用加鎖的方式牺丙,保證只分配一次存儲(chǔ)空間
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
/*
1. mutableCopy 創(chuàng)建一個(gè)新的可變對(duì)象则涯,并初始化為原對(duì)象的值,新對(duì)象的引用計(jì)數(shù)為 1冲簿;
2. copy 返回一個(gè)不可變對(duì)象粟判。分兩種情況:(1)若原對(duì)象是不可變對(duì)象,那么返回原對(duì)象峦剔,并將其引用計(jì)數(shù)加 1 档礁;(2)若原對(duì)象是可變對(duì)象,那么創(chuàng)建一個(gè)新的不可變對(duì)象吝沫,并初始化為原對(duì)象的值呻澜,新對(duì)象的引用計(jì)數(shù)為 1。
*/
//讓代碼更加的嚴(yán)謹(jǐn)
-(nonnull id)copyWithZone:(nullable NSZone *)zone
{
// return [[self class] allocWithZone:zone];
return _instance;
}
-(nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
{
return _instance;
}
- 1.3 MRC實(shí)現(xiàn)單例
(1)實(shí)現(xiàn)步驟
01 在類的內(nèi)部提供一個(gè)static修飾的全局變量
02 提供一個(gè)類方法野舶,方便外界訪問(wèn)
03 重寫+allocWithZone方法易迹,保證永遠(yuǎn)都只為單例對(duì)象分配一次內(nèi)存空間
04 嚴(yán)謹(jǐn)起見(jiàn),重寫-copyWithZone方法和-MutableCopyWithZone方法
05 重寫release方法
06 重寫retain方法
07 建議在retainCount方法中返回一個(gè)最大值
(2)配置MRC環(huán)境知識(shí)
01 注意ARC不是垃圾回收機(jī)制平道,是編譯器特性
02 配置MRC環(huán)境:build setting ->搜索automatic ref->修改為NO
(3)相關(guān)代碼
//提供一個(gè)static修飾的全局變量睹欲,強(qiáng)引用著已經(jīng)實(shí)例化的單例對(duì)象實(shí)例
static XMGTools *_instance;
//類方法,返回一個(gè)單例對(duì)象
+(instancetype)shareTools
{
//注意:這里建議使用self,而不是直接使用類名Tools(考慮繼承)
return [[self alloc]init];
}
//保證永遠(yuǎn)只分配一次存儲(chǔ)空間
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
//使用GCD中的一次性代碼
// static dispatch_once_t onceToken;
// dispatch_once(&onceToken, ^{
// _instance = [super allocWithZone:zone];
// });
//使用加鎖的方式一屋,保證只分配一次存儲(chǔ)空間
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
//讓代碼更加的嚴(yán)謹(jǐn)
-(nonnull id)copyWithZone:(nullable NSZone *)zone
{
// return [[self class] allocWithZone:zone];
return _instance;
}
-(nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
{
return _instance;
}
//在MRC環(huán)境下窘疮,如果用戶retain了一次,那么直接返回instance變量冀墨,不對(duì)引用計(jì)數(shù)器+1
//如果用戶release了一次闸衫,那么什么都不做,因?yàn)閱卫J皆谡麄€(gè)程序運(yùn)行過(guò)程中都擁有且只有一份诽嘉,程序退出之后被釋放蔚出,所以不需要對(duì)引用計(jì)數(shù)器操作
-(oneway void)release
{
}
-(instancetype)retain
{
return _instance;
}
//慣用法弟翘,有經(jīng)驗(yàn)的程序員通過(guò)打印retainCount這個(gè)值可以猜到這是一個(gè)單例
-(NSUInteger)retainCount
{
return MAXFLOAT;
}
- 1.4 通用版本
(1)有意思的對(duì)話
01 問(wèn):寫一份單例代碼在ARC和MRC環(huán)境下都適用?
答:可以使用條件編譯來(lái)判斷當(dāng)前項(xiàng)目環(huán)境是ARC還是MRC
02 問(wèn):條件編譯的代碼呢骄酗,么么噠稀余?
//答:條件編譯
#if __has_feature(objc_arc)
//如果是ARC,那么就執(zhí)行這里的代碼1
#else
//如果不是ARC趋翻,那么就執(zhí)行代理的代碼2
#endif
03 問(wèn):在項(xiàng)目里面往往需要實(shí)現(xiàn)很多的單例睛琳,比如下載、網(wǎng)絡(luò)請(qǐng)求踏烙、音樂(lè)播放等等师骗,弱弱的問(wèn)一句單例可以用繼承嗎?
答:?jiǎn)卫遣豢梢杂美^承的讨惩,如果想一次寫就辟癌,四處使用,那么推薦親使用帶參數(shù)的宏定義啦步脓!
04 問(wèn):宏定義怎么弄愿待?
答:這個(gè)嘛~~回頭看一眼我的代碼咯浩螺,親靴患。
(2)使用帶參數(shù)的宏完成通用版單例模式代碼
01 注意條件編譯的代碼不能包含在宏定義里面
02 宏定義的代碼只需要寫一次就好,之后直接拖到項(xiàng)目中用就OK
2.NSOperation
- 2.1 NSOperation基本使用
(1)相關(guān)概念
01 NSOperation是對(duì)GCD的包裝
02 兩個(gè)核心概念【隊(duì)列+操作】
(2)基本使用
01 NSOperation本身是抽象類要出,只能只有它的子類
02 三個(gè)子類分別是:NSBlockOperation鸳君、NSInvocationOperation以及自定義繼承自NSOperation的類
03 NSOperation和NSOperationQueue結(jié)合使用實(shí)現(xiàn)多線程并發(fā)
(3)相關(guān)代碼
// 01 NSInvocationOperation
//1.封裝操作
/*
第一個(gè)參數(shù):目標(biāo)對(duì)象
第二個(gè)參數(shù):該操作要調(diào)用的方法,最多接受一個(gè)參數(shù)
第三個(gè)參數(shù):調(diào)用方法傳遞的參數(shù)患蹂,如果方法不接受參數(shù)或颊,那么該值傳nil
*/
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self selector:@selector(run) object:nil];
//2.啟動(dòng)操作
[operation start];
-------------------------------------------------
// 02 NSBlockOperation
//1.封裝操作
/*
NSBlockOperation提供了一個(gè)類方法,在該類方法中封裝操作
*/
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
//在主線程中執(zhí)行
NSLog(@"---download1--%@",[NSThread currentThread]);
}];
//2.追加操作传于,追加的操作在子線程中執(zhí)行
[operation addExecutionBlock:^{
NSLog(@"---download2--%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"---download3--%@",[NSThread currentThread]);
}];
//3.啟動(dòng)執(zhí)行操作
[operation start];
----------------------------------------------
// 03 自定義NSOperation
//如何封裝操作囱挑?
//自定義的NSOperation,通過(guò)重寫內(nèi)部的main方法實(shí)現(xiàn)封裝操作
-(void)main
{
NSLog(@"--main--%@",[NSThread currentThread]);
}
//如何使用?
//1.實(shí)例化一個(gè)自定義操作對(duì)象
XMGOperation *op = [[XMGOperation alloc]init];
//2.執(zhí)行操作
[op start];
- 2.2 NSOperationQueue基本使用
(1)NSOperation中的兩種隊(duì)列
01 主隊(duì)列 通過(guò)mainQueue獲得沼溜,凡是放到主隊(duì)列中的任務(wù)都將在主線程執(zhí)行
02 非主隊(duì)列 直接alloc init出來(lái)的隊(duì)列平挑。非主隊(duì)列同時(shí)具備了并發(fā)和串行的功能,通過(guò)設(shè)置最大并發(fā)數(shù)屬性來(lái)控制任務(wù)是并發(fā)執(zhí)行還是串行執(zhí)行
(2)相關(guān)代碼
//自定義NSOperation
-(void)customOperation
{
//1.創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.封裝操作
//好處:1.信息隱蔽
//2.代碼復(fù)用
XMGOperation *op1 = [[XMGOperation alloc]init];
XMGOperation *op2 = [[XMGOperation alloc]init];
//3.添加操作到隊(duì)列中
[queue addOperation:op1];
[queue addOperation:op2];
}
//NSBlockOperation
- (void)block
{
//1.創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.封裝操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1----%@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2----%@",[NSThread currentThread]);
}];
[op2 addExecutionBlock:^{
NSLog(@"3----%@",[NSThread currentThread]);
}];
[op2 addExecutionBlock:^{
NSLog(@"4----%@",[NSThread currentThread]);
}];
//3.添加操作到隊(duì)列中
[queue addOperation:op1];
[queue addOperation:op2];
//補(bǔ)充:簡(jiǎn)便方法
[queue addOperationWithBlock:^{
NSLog(@"5----%@",[NSThread currentThread]);
}];
}
//NSInvocationOperation
- (void)invocation
{
/*
GCD中的隊(duì)列:
串行隊(duì)列:自己創(chuàng)建的系草,主隊(duì)列
并發(fā)隊(duì)列:自己創(chuàng)建的通熄,全局并發(fā)隊(duì)列
NSOperationQueue
主隊(duì)列:[NSOperationQueue mainqueue];凡事放在主隊(duì)列中的操作都在主線程中執(zhí)行
非主隊(duì)列:[[NSOperationQueue alloc]init],并發(fā)和串行找都,默認(rèn)是并發(fā)執(zhí)行的
*/
//1.創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.封裝操作
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download2) object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download3) object:nil];
//3.把封裝好的操作添加到隊(duì)列中
[queue addOperation:op1];//[op1 start]
[queue addOperation:op2];
[queue addOperation:op3];
}
- 2.3 NSOperation其它用法
(1)設(shè)置最大并發(fā)數(shù)【控制任務(wù)并發(fā)和串行】
//1.創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.設(shè)置最大并發(fā)數(shù)
//注意點(diǎn):該屬性需要在任務(wù)添加到隊(duì)列中之前進(jìn)行設(shè)置
//該屬性控制隊(duì)列是串行執(zhí)行還是并發(fā)執(zhí)行
//如果最大并發(fā)數(shù)等于1唇辨,那么該隊(duì)列是串行的,如果大于1那么是并行的
//系統(tǒng)的最大并發(fā)數(shù)有個(gè)默認(rèn)的值能耻,為-1赏枚,如果該屬性設(shè)置為0亡驰,那么不會(huì)執(zhí)行任何任務(wù)
queue.maxConcurrentOperationCount = 2;
(2)暫停和恢復(fù)以及取消
//設(shè)置暫停和恢復(fù)
//suspended設(shè)置為YES表示暫停,suspended設(shè)置為NO表示恢復(fù)
//暫停表示不繼續(xù)執(zhí)行隊(duì)列中的下一個(gè)任務(wù)饿幅,暫停操作是可以恢復(fù)的
if (self.queue.isSuspended) {
self.queue.suspended = NO;
}else
{
self.queue.suspended = YES;
}
//取消隊(duì)列里面的所有操作
//取消之后隐解,當(dāng)前正在執(zhí)行的操作的下一個(gè)操作將不再執(zhí)行,而且永遠(yuǎn)都不在執(zhí)行诫睬,就像后面的所有任務(wù)都從隊(duì)列里面移除了一樣
//取消操作是不可以恢復(fù)的
[self.queue cancelAllOperations];
---------自定義NSOperation取消操作--------------------------
-(void)main
{
//耗時(shí)操作1
for (int i = 0; i<1000; i++) {
NSLog(@"任務(wù)1-%d--%@",i,[NSThread currentThread]);
}
NSLog(@"+++++++++++++++++++++++++++++++++");
//蘋果官方建議煞茫,每當(dāng)執(zhí)行完一次耗時(shí)操作之后,就查看一下當(dāng)前隊(duì)列是否為取消狀態(tài)摄凡,如果是续徽,那么就直接退出
//好處是可以提高程序的性能
if (self.isCancelled) {
return;
}
//耗時(shí)操作2
for (int i = 0; i<1000; i++) {
NSLog(@"任務(wù)1-%d--%@",i,[NSThread currentThread]);
}
NSLog(@"+++++++++++++++++++++++++++++++++");
}
- 2.4 NSOperation實(shí)現(xiàn)線程間通信
(1)開子線程下載圖片
//1.創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.使用簡(jiǎn)便方法封裝操作并添加到隊(duì)列中
[queue addOperationWithBlock:^{
//3.在該block中下載圖片
NSURL *url = [NSURL URLWithString:@"http://news.51sheyuan.com/uploads/allimg/111001/133442IB-2.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
NSLog(@"下載圖片操作--%@",[NSThread currentThread]);
//4.回到主線程刷新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
NSLog(@"刷新UI操作---%@",[NSThread currentThread]);
}];
}];
(2)下載多張圖片合成綜合案例(設(shè)置操作依賴)
//02 綜合案例
- (void)download2
{
NSLog(@"----");
//1.創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//2.封裝操作下載圖片1
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/zhidao/pic/item/6a63f6246b600c3320b14bb3184c510fd8f9a185.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
//拿到圖片數(shù)據(jù)
self.image1 = [UIImage imageWithData:data];
}];
//3.封裝操作下載圖片2
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://pic.58pic.com/58pic/13/87/82/27Q58PICYje_1024.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
//拿到圖片數(shù)據(jù)
self.image2 = [UIImage imageWithData:data];
}];
//4.合成圖片
NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
//4.1 開啟圖形上下文
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
//4.2 畫image1
[self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];
//4.3 畫image2
[self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];
//4.4 根據(jù)圖形上下文拿到圖片數(shù)據(jù)
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// NSLog(@"%@",image);
//4.5 關(guān)閉圖形上下文
UIGraphicsEndImageContext();
//7.回到主線程刷新UI
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
self.imageView.image = image;
NSLog(@"刷新UI---%@",[NSThread currentThread]);
}];
}];
//5.設(shè)置操作依賴
[combine addDependency:op1];
[combine addDependency:op2];
//6.添加操作到隊(duì)列中執(zhí)行
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:combine];
}
3.多圖下載綜合示例程序
(1)涉及知識(shí)點(diǎn)
01 字典轉(zhuǎn)模型
02 存儲(chǔ)數(shù)據(jù)到沙盒,從沙盒中加載數(shù)據(jù)
03 占位圖片的設(shè)置(cell的刷新問(wèn)題)
04 如何進(jìn)行內(nèi)存緩存(使用NSDictionary)
05 在程序開發(fā)過(guò)程中的一些容錯(cuò)處理
06 如何刷新tableView的指定行(解決數(shù)據(jù)錯(cuò)亂問(wèn)題)
07 NSOperation以及線程間通信相關(guān)知識(shí)
4.第三方框架
(1)SDWebImage基本使用
01 設(shè)置imageView的圖片
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.icon] placeholderImage:[UIImage imageNamed:@"placehoder"]];
02 設(shè)置圖片并計(jì)算下載進(jìn)度
//下載并設(shè)置圖片
/*
第一個(gè)參數(shù):要下載圖片的url地址
第二個(gè)參數(shù):設(shè)置該imageView的占位圖片
第三個(gè)參數(shù):傳一個(gè)枚舉值亲澡,告訴程序你下載圖片的策略是什么
第一個(gè)block塊:獲取當(dāng)前圖片數(shù)據(jù)的下載進(jìn)度
receivedSize:已經(jīng)下載完成的數(shù)據(jù)大小
expectedSize:該文件的數(shù)據(jù)總大小
第二個(gè)block塊:當(dāng)圖片下載完成之后執(zhí)行該block中的代碼
image:下載得到的圖片數(shù)據(jù)
error:下載出現(xiàn)的錯(cuò)誤信息
SDImageCacheType:圖片的緩存策略(不緩存钦扭,內(nèi)存緩存,沙盒緩存)
imageURL:下載的圖片的url地址
*/
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.icon] placeholderImage:[UIImage imageNamed:@"placehoder"] options:SDWebImageRetryFailed progress:^(NSInteger receivedSize, NSInteger expectedSize) {
//計(jì)算當(dāng)前圖片的下載進(jìn)度
NSLog(@"%.2f",1.0 *receivedSize / expectedSize);
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
}];
03 系統(tǒng)級(jí)內(nèi)存警告如何處理(面試)
//取消當(dāng)前正在進(jìn)行的所有下載操作
[[SDWebImageManager sharedManager] cancelAll];
//清除緩存數(shù)據(jù)(面試)
//cleanDisk:刪除過(guò)期的文件數(shù)據(jù)床绪,計(jì)算當(dāng)前未過(guò)期的已經(jīng)下載的文件數(shù)據(jù)的大小客情,如果發(fā)現(xiàn)該數(shù)據(jù)大小大于我們?cè)O(shè)置的最大緩存數(shù)據(jù)大小,那么程序內(nèi)部會(huì)按照按文件數(shù)據(jù)緩存的時(shí)間從遠(yuǎn)到近刪除癞己,知道小于最大緩存數(shù)據(jù)為止膀斋。
//clearMemory:直接刪除文件,重新創(chuàng)建新的文件夾
//[[SDWebImageManager sharedManager].imageCache cleanDisk];
[[SDWebImageManager sharedManager].imageCache clearMemory];
04 SDWebImage默認(rèn)的緩存時(shí)間是1周
05 如何播放gif圖片
/*
5-1 把用戶傳入的gif圖片->NSData
5-2 根據(jù)該Data創(chuàng)建一個(gè)圖片數(shù)據(jù)源(NSData->CFImageSourceRef)
5-3 計(jì)算該數(shù)據(jù)源中一共有多少幀痹雅,把每一幀數(shù)據(jù)取出來(lái)放到圖片數(shù)組中
5-4 根據(jù)得到的數(shù)組+計(jì)算的動(dòng)畫時(shí)間-》可動(dòng)畫的image
[UIImage animatedImageWithImages:images duration:duration];
*/
06 如何判斷當(dāng)前圖片類型
+ (NSString *)sd_contentTypeForImageData:(NSData *)data;
(2)SDWebImage內(nèi)部結(jié)構(gòu)