/*--------------------------- NSOperation綜合案例: 1.項目簡介/UI 搭建 ----------------------------*/
重點:
1. UI 界面;
2.構建數據模型;
3.緩存開發中須要用到的數據模型.
4.在懶加載方法中,不要出現點語法.
{
1. 搭建 UI 界面
//導航控制器 + 表格視圖控制器(根控制器)
//注意提供一個字典轉模型的方法.
// KVC(Key - Value - Code)鍵值編碼使用注意
// 注意與 KVO (Key - Value - Observe)鍵值監聽的區別;
存放:
3. 將數據模型緩存到可變數組中,開發的數據直接來源於這個可變數組
//懶加載存放數據模型的數組.操做步驟:
「apps ———> 數據模型"
//懶加載存放數據模型的數組.操做步驟:
(1) 將apps.plist 文件轉換爲數組 (數組中存放的是數據字典): array ——> 字典模型
字典轉模型
<1> 得到 apps.plist 文件 的路徑(知道了文件路徑,就可以找到文件)
{
//獲取 apps.plist 文件名的路徑
NSString *path = [[NSBundle mainBundle]pathForResource:@「apps.plist」 ofType:nil];
}
<2> 根據 apps.plist 文件 轉換爲數組
{
//根據 apps.plist 文件 轉換爲數組:array 中存儲的是數組字典
NSArray *array = [NSArray arrayWithContentsOfFile:path];
}
}
(2) 取出數據字典,將數據字典轉換爲模型,而且將模型存放在apps數組中
{
//取出數據字典,將數據字典轉換爲模型
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx,BOOL *stop{
//array :存放字典模型
//obj :數組中存放的字典
//idx : obj在數組中的位置
//<1> 取出數組中的數據字典
NSDictionary *dict = obj;
//<2> 將數據字典轉換爲數據模型
ITApp *app = [ITAppWithDictionary:dict];
// 將數據模型添加到臨時可變數組中
[appArray addObject:app];
}];
//將數據模型存放在 apps 中
_apps = appsArray;
}
//注意,取出數組中的字典有兩種方式:<1> for; <2> Block.推薦使用第二種.效率更好.
#pragma 懶加載
//存放數據模型的數組
-(NSMutableArray *)apps
{
if(!_apps){
_apps = [NSMutableArray array];
//獲取 apps.plist 文件名的全路徑
NSString * path = [[NSBundle mainBundle]pathForResource: @「apps.plist」 ofFile:nil];
//根據apps.plist文件 轉換爲數組:array 中存儲的是數據字典
NSArray * array = [NSArray arrayWithContentsOfFile:path];
//打印一下數組字典
// NSLog(@「%@「,array);
//定義一個臨時的可變數組來存放數據模型:
NSMutableArray *appArray = [NSMutableArray array];
//拿出數據字典,將數據字典轉換爲模型,而且將模型存放到apps數組中:
//兩種方法:
<1> for 循環
for (int i = 0 ;i < array.count; i++)
{
//取出array 中存放的數據字典
NSDictionary * dict = array[i];
// 將數據字典轉換爲數據模型
ITApp *app = [ITApp ITAppWithDictionary:app];
//將數據模型添加到臨時可變數組中
[appsArray addObject:app];
};
<2> Block
[array enumerateObjectsUsingBlock:^(id obj
NSUInteger idx,BOOL *stop){
//obj: 數組中存放的字典; idx :obj在數組中的位置
NSDictionary *dict = obj;
//將數據字典轉換爲數據模型
ITApp * app = [ITApp ITAppWithDictionary:dict];
//將數據模型添加到臨時可變數組中
[appsArray addObject:app];
}];
//將數據模型添加到臨時可變數組中
_apps = appsArray;
}
return _apps;
}
4 注意點:UITableView 的數據源方法;
-(UITableViewCell *)tableView:(UITableView *)tableViewcellRowAtIndexPath:(NSIndexPath *)indexPath{
必須返回一個: UITableViewCell
//調試的時候.用下面的這種方式寫,不會報錯
return [UITableViewCell new];
}
/*--------------------------- NSOperation綜合案例: 2.下載圖片/技術選擇 ----------------------------*/
重點:
1.分析項目需求:
2.處理內存警告,優化用戶體驗;
3.技術選擇,技術實施,代碼編寫.
4.Bug
{
項目需求:
<1> 下載圖片
分析:
1>子線程下載圖片,主線程顯示圖片
2>開啓子線程有三種技術方案可供選擇:
(1) NSThread
(2) GCD
(3) NSOperation 配合 NSOperationQueue使用
<2> 內存警告處理
分析:
接受到內存警告的時候,中止一切下載操做,防止閃退
<3> 用戶體驗
分析:
再與用戶作 UI 交互的時候,最好暫停圖片的下載;用戶滾動結束以後,再繼續下載圖片
1.技術選擇:(3) NSOperation 配合 NSOperationQueue使用
2.技術點實施
//<1> 用戶開始滾動的時候,暫停下載操做;中止滾動以後,恢復下載操做
{
#pragma UIScrollerViewDelegate 代理協議
//開始滾動的時候調用
-(void)scrollerViewWillBeginDragging:(UIScrollerView *)scrollerView
{
NSLog(@「暫停下載---");
//暫停全部下載操做
[self.queue setSuspended:YES];
}
//滾動結束的時候調用
-(void)scrollerViewDidEndDragging:(UIScrollerView *)scrollerView willDecelerate:(BOOL)decelerate
{
NSLog(@「恢復下載==");
//恢復全部下載操做
[self.queue setSuspended:NO];
}
}
//<2> 接受到內存警告的時候,取消一切操做
{
//接受到內存警告的時候調用
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
//取消一切下載操做
[self.queue cancelAllOperations];
}
}
//<3> 將下載圖片的操做封裝在 NSBlockOperation.最後將操做放在併發隊列中,自動執行
{
__weak type(self)wself = self;
//定義下載操做
NSBlockOperation *o = [NSBlockOperation blockOperationWithBlock:^」{
//下載網絡圖片
UIImage *webImage = [wself downloadWebImage:app.icon];
//回到主線程顯示圖片
[NSOperationQueue mainQueue]addOperationWithBlock:^{
//顯示圖片
cell.imageView.image = webImage;
}];
}];
//將下載操做放在併發隊列中,自動開啓下載
[self.queue addOperation:op];
}
3.運行程序以後,發現3個 Bug
<1> 程序運行以後,圖片不會直接顯示出來,須要刷新以後才能顯示(滾動/點擊都會重繪UI)
<2> 圖片錯位問題
<3> 用戶體驗方面的Bug:只要滾動,圖片就會重複下載,即便已經下載好的圖片,也會從新下載(耗費流量,體驗巨差,巨耗電)
/*------------------------------ NSOperation綜合案例: 3.Bug 解決 ---------------------------------*/
重點.1.分析Bug產生的緣由並解決Bug. 2.知識點回顧:如何防止一個url對應的圖片重複下載?
{
Bug 產生的緣由分析:
<1> 程序運行以後,圖片不會直接顯示出來,須要刷新以後才能顯示(滾動/點擊都會重繪 UI)
Bug產生的緣由:UITableViewCell 中剛開始沒有設置圖片的 Frame ,圖片下載完後,點擊刷新以後,就會從新繪製, UITableViewCell,這樣就會顯示圖片了
解決 Bug: 下載以前最好把圖片的frame 繪製出來.好比,能夠添加一張佔位圖片
//設置佔位圖片的代碼
cell.imageView.image = [UIImage imageName:@「佔位圖片的名字"];
}
//圖片錯位問題
Bug產生的緣由, UITableViewCell 的重用以及網絡下延時產生的.
解決 Bug: 讓數據控制視圖(視圖根據數據來顯示出來)設置一個圖片緩存 cell 中的圖片來源於這個圖片緩存
{
//設置圖片緩存.
定義一個字典爲圖片緩存,保存下載好的圖片(以圖片的 url 做爲 Key值,如下載好的圖片爲 Value)
//能夠選擇NSCache 代替字典做爲緩存機制
//NSCache在內存緊張的時候,會自動釋放一些資源(自動銷燬圖片,咱們沒法控制)
//若是使用字典,在接受到內存警告以後,須要手動釋放資源
<2> 從緩存中取出 cell 對應的圖片
cell 設置圖片:根據 cell 的app.icon(url) 從字典中取出對應的圖片
<3> 圖片下載完畢以後刷新所在行的數據 的代碼
[tableView reloadRowsAtIndexPaths:@[indexPath ] withRowAnimation:UITableViewRowAnimationNone];
🐷1⃣️ :不必刷新整個表格視圖,刷新所在行額數據就能夠了
}
<3> 用戶體驗方面的Bug :只要滾動,圖片就會重複下載,即便已經下載好的圖片,也會從新下載.(耗費流量,體驗巨差,巨耗電)
Bug產生緣由: 只要上下滾動,就會調用 - (UITableViewCell *)tableView:(UITableView*tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
這個方法.這個方法頻繁調用致使下載操做一直建立.
解決 Bug:每次建立下載操做以前,最好先檢查一下,下載操做是否存在,對於已經存在的操做,不要重複下載
//解決方法
{
<1> 設置操做緩存定義一個字典做爲下載操做緩存,保存已經建立好的下載操做,(一樣,以圖片的 url 爲Key值:以操做Operation 爲Value)
<2> 從緩存中取出操做比較
每次從新建立下載操做以前,首先從下載操做緩存之中查看操做是否已經存在.若是存在,就不要再次建立:若是不存在,建立新的下載操做
<3> 防止操做緩存越存越大
圖片下載成功以後,就下載操做就沒有存在的意義了,應該及時清除緩存,防止下載操做緩存愈來愈大
}
關於NSCache Cache儲藏保存的的意思
{
//能夠選擇 NSCache 代替字典做爲緩存機制
NSCache 類結合了各類自動刪除策略,以確保不會佔用過多的系統內存.若是其餘應用須要內存時,系統自動執行這些策略
NSCache是線程安全的,咱們能夠在不一樣的線程中添加/刪除和查詢緩存中的對象,而不須要鎖定緩存區域
/*------------------------ NSOperation綜合案例: 4.完善項目-添加沙盒緩存 -----------------------------*/
重點:1. 處理內存警告 2.添加沙盒緩存:
1> 因爲添加了內存緩存機制(圖片緩存和操做緩存) 在接受到內存警告的時候,最好釋放內存緩存
將圖片緩存在沙盒中,能夠優化用戶體驗,之後每次展現圖片,對於已經下載過的圖片,就不須要從新下載
2.添加沙盒緩存.
<1> 認識沙盒
默認狀況下,每一個沙盒含有三個文件夾:
Documents 文件
Library 軟件庫
tmp
Documents :蘋果建議將程序中創建的或在程序中瀏覽到的文件數據保存在該目錄下,iTunes備份和恢復的時候會包括此目錄
Library : 存儲程序的默認設置或其餘狀態信息
//Library/Caches: 存放緩存文件, iTunes 不會備份此目錄,此目錄下文件不會在應用退出刪除
tmp:提供一個即時建立臨時文件的地方
<2> 將圖片寫入沙盒:將文件存儲在 Library/Caches路徑
1>獲取 /Caches路徑
{
NSString * path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES).lastObject;
}
2>拼接文件路徑
{
//拼接文件路徑(path + app .lastPathComponent ),以url中圖片的名做爲名字
NSString *path = [path stringByAppendingPathComponent:app.icon.lastPathComponent];
}
3> 將 image 轉換爲二進制數據.//沙盒中不能直接存取圖片數據
{
//將 UIImage 圖片轉換爲二進制數據
NSData *data = UIImagePNGRepresentation(webImage);
}
4>將圖片的二進制數據存入沙盒
{
//將圖片的二進制數據存入沙盒,路徑:file
[data writeToFile:file atomically:YES];
}
<3> 從沙盒中獲取圖片
1>獲取 / Caches路徑
2>拼接完整的圖片文件路徑
3>根據完整的圖片文件路徑獲取圖片
{
UIImage *image = [UIImage imageWithContentsOfFile:fileName];
}
}
/*----------------------------- NSOperation綜合案例: 5.SDWebImage使用 ------------------------------*/
重點:1: SWWebImage
{
1.SDWebImage 簡介:
SDWebImage 是一個開源的第三方庫 ,他提供了UIImageView分類,以支持從遠程服務器下載緩存圖片的功能
<1> SDWebImageManager
在實際的運用中,咱們並不直接使用SDWebImageDownloader類(網絡圖片下載類)以及SDImageCache類 (網絡圖片緩存類)來執行圖片的下載及緩存.
爲了方便用戶的使用,SDWebImage提供了SDWebImageManager對象來管理圖片的下載與緩存
咱們常常用到的諸如UIImageView + WebCache等控件的分類都是基於SDWebImageManager對象的
該對象將一個下載器和一個圖片緩存綁定在一塊兒,並對外提供兩個只讀屬性來獲取他們
<2> UIImageView+WebCache
咱們在使用SDWebImage 的時候,使用的最多的是UIImageView + WebCache中的針對的擴展方法,這些擴展方法將UIImageView與WebCache集成在一塊兒,來讓UIImageView對象擁有異步下載和緩存遠程圖片的能力
其中最核心的方法是:-sd_setImageWithURL:placeholderImage:options:progress:completed:其使用SDWebImageManager單例對象下載並緩存圖片,完成後將圖片賦值給UIImageViw對像那個的image屬性.以使圖片顯示出來
//👺👺👺👺👺👺👺👺👺👺
1>SDWebImage的默認緩存是多長時間?
答:一個星期
2>SDWebImage的默認最大併發數是多少?
答:6條
3>SDWebImage底層是怎麼實現的?
答:cell下載圖片思路 - 有沙盒緩存
3.SDWebImage經常使用方法;
1> 經常使用方法
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder;
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options;
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock;
2>SDWebImageOptions
* SDWebImageRetryFailed : 下載失敗後,會自動從新下載
*SDWebImageLowPriority :當正在進行 UI 交互時,自動暫停內部的一些下載操做
*SDWebImageRetryFailed | SDWebImagePriority:擁有上面2個功能
3>內存處理: 當app接收到內存警告時
💼💼💼💼💼💼💼 當app接收到內存警告
-(void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
SDWebImageManager * mgr = [SDWebImageManager sharedManager];
//1.取消正在下載的操做
[mgr cancelAll];
//2. 清楚內存緩存
[mgr.imageCache clearMemory];
}
}
/*----------------------------- NSOperation綜合案例: 6.自定義 NSOperation ----------------------------*/
{
自定義 NSOperation 的步驟;
重點 -(void)main 方法,在裏面實現想執行的操做
重寫 -(void)main 方法注意點:
1>本身建立自動釋放池(若是異步操做,沒法訪問主線程的自動釋放池)
2>常常經過 - (BOOL)isCancelled 方法檢查操做是否取消,對取消作出相應
}