SDWebImage框架詳解
- 下載圖片并顯示:
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:appM.icon] placeholderImage:[UIImage imageNamed:@"Snip20200808_172"]];
- 下載圖片/顯示圖片/內(nèi)存緩存/磁盤緩存
-(void)download1
{
/*
第一個參數(shù):要下載圖片的URL
第二個參數(shù):占位圖片
第三個參數(shù):下載選項
第四個參數(shù):progress 進度回調(diào)
receivedSize:已經(jīng)下載的數(shù)據(jù)大小
expectedSize:圖片的中大小
第五個參數(shù):completed 完成回調(diào)(成功|失敗)
cacheType:是否使用了緩存,使用的方式
*/
[self.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://ww1.sinaimg.cn/crop.0.0.720.720.1024/abe7c97cjw8ermn0v2x7nj20k00k0jrz.jpg"] placeholderImage:[UIImage imageNamed:@"Snip20200808_11"] options:SDWebImageLowPriority | SDWebImageCacheMemoryOnly progress:^(NSInteger receivedSize, NSInteger expectedSize) {
NSLog(@"%f",(CGFloat)receivedSize/expectedSize);
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
}];
}
- 下載圖片\內(nèi)存緩存\磁盤緩存
-(void)download2
{
[[SDWebImageManager sharedManager]downloadImageWithURL:[NSURL URLWithString:@"http://img.kumi.cn/photo/6b/42/eb/6b42eb5597c4f174.jpg"] options:kNilOptions progress:^(NSInteger receivedSize, NSInteger expectedSize) {
NSLog(@"%f",(CGFloat)receivedSize/expectedSize);
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (error == nil) {
self.imageView.image = image;
}
}];
}
- 下載圖片(完成后回調(diào)是在子線程中完成處理的)
-(void)download3
{
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:[NSURL URLWithString:@"http://img.kumi.cn/photo/6b/42/eb/6b42eb5597c4f174.jpg"] options:kNilOptions progress:^(NSInteger receivedSize, NSInteger expectedSize) {
} completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
//completed是在子線程中處理的
dispatch_async(dispatch_get_main_queue(), ^{
//設(shè)置圖片
self.imageView.image = image;
});
}];
}
- 顯示gif動畫
self.imageView.image = [UIImage sd_animatedGIFNamed:@"1234"];
- 接受到系統(tǒng)內(nèi)存警告時如何處理:
//(1)取消當前正在進行的所有下載操作 [[SDWebImageManager sharedManager] cancelAll]; //(2)清除緩存數(shù)據(jù) //cleanDisk:刪除過期的文件數(shù)據(jù)采驻,計算當前未過期的已經(jīng)下載的文件數(shù)據(jù)的大小挂洛,如果發(fā)現(xiàn)該數(shù)據(jù)大小大于我們設(shè)置的最大緩存數(shù)據(jù)大小换团,那么程序內(nèi)部會按照按文件數(shù)據(jù)緩存的時間從遠到近刪除协屡,知道小于最大緩存數(shù)據(jù)為止。 //clearMemory:直接刪除文件茸俭,重新創(chuàng)建新的文件夾 //[[SDWebImageManager sharedManager].imageCache cleanDisk]; [[SDWebImageManager sharedManager].imageCache clearMemory];
- SDWebImage內(nèi)部實現(xiàn)細節(jié):
- 判斷圖片當前類型:之判斷二進制數(shù)據(jù)的第一個字節(jié)
- 默認緩存周期:一周
- 緩存策略:默認進行內(nèi)存和磁盤緩存吊履,下載時首先檢查內(nèi)存緩存,其次是磁盤緩存
- 緩存實現(xiàn)方式:采用了蘋果推出的用來處理緩存的NSCache
- 對內(nèi)存警告的處理:框架內(nèi)部監(jiān)聽系統(tǒng)內(nèi)存警告的通知调鬓,當發(fā)生時艇炎,自動移除緩存中的所有對象
- 下載隊列中對多個圖片任務(wù)采取的措施:方式有FIFO以及LIFO兩種方式,默認是FIFO
- 框架內(nèi)允許的最大并發(fā)數(shù)6
- 磁盤緩存圖片的命名:對圖片url進行md5散列加密(【echo -n "url" |MD5】)
NSCache詳解
- NSCache簡單說明:
- NSCache是蘋果用來管理內(nèi)存的類腾窝,類似于MutibleArray缀踪,在SDWebImage和AFN等框架中廣泛用來管理緩存
- NSCache在內(nèi)存過低時會自動釋放對象(我們要手動釋放對象)
- NSCache是線程安全的居砖,在使用過程中不需要加鎖
- NSCache的Key只是對對象進行Strong引用,不是拷貝驴娃,在清理的時候計算的是實際大小而不是引用的大凶嗪颉(不明白)
- NSCache屬性以及方法介紹
- 1)屬性介紹
- name:名稱
- delegete:設(shè)置代理
- totalCostLimit:緩存空間的最大總成本,超出上限會自動回收對象唇敞。默認值為0蔗草,表示沒有限制
- countLimit:能夠緩存的對象的最大數(shù)量。默認值為0疆柔,表示沒有限制
- evictsObjectsWithDiscardedContent:標識緩存是否回收廢棄的內(nèi)容
- 2)方法介紹
objc - (void)setObject:(ObjectType)obj forKey:(KeyType)key;//在緩存中設(shè)置指定鍵名對應(yīng)的值咒精,0成本 - (void)setObject:(ObjectType)obj forKey:(KeyType)keycost:(NSUInteger)g; //在緩存中設(shè)置指定鍵名對應(yīng)的值,并且指定該鍵值對的成本旷档,用于計算記錄在緩存中的所有對象的總成本 //當出現(xiàn)內(nèi)存警告或者超出緩存總成本上限的時候模叙,緩存會開啟一個回收過程,刪除部分元素 - (void)removeObjectForKey:(KeyType)key;//刪除緩存中指定鍵名的對象 - (void)removeAllObjects;//刪除緩存中所有的對象
位移的簡單說明
- 常見的幾種枚舉形式:
//枚舉一
typedef enum{
XMGDemoTypeTop,
XMGDemoTypeBottom,
}XMGDemoType;
//枚舉二
typedef NS_ENUM(NSInteger,XMGType)
{
XMGTypeTop,
XMGTypeBottom,
};
//枚舉三:位移枚舉
typedef NS_OPTIONS(NSInteger, XMGActionType)
{
XMGActionTypeTop = 1<<0,
XMGActionTypeBottom = 1<<1,
XMGActionTypeLeft = 1<<2,
XMGActionTypeRight = 1 <<3,
};
- 位移枚舉相關(guān)說明
- 特點:通過使用位移枚舉可以實現(xiàn)一個參數(shù)實現(xiàn)傳遞多個操作
- 原理:按位與只要有0則為0彬犯,按位或只要有1則為1
- 技巧:如果位移枚舉的第一個選項為0向楼,那么在傳遞參數(shù)的時候默認可以傳0,傳0性能最優(yōu)谐区,不做額外的操作
RunLoop介紹:
基礎(chǔ)知識:
基本作用:
保證程序不退出(死循環(huán))
處理各種事件(觸摸事件、定時器事件逻卖、selector事件等)
節(jié)省cpu資源宋列,提高程序性能,該運行時運行评也,該休息時休息
-
RunLoop對象
- 在iOS開發(fā)中有兩套api來訪問Runloop
foundation框架【NSRunloop】
core foundation框架【CFRunloopRef】 - NSRunLoop和CFRunLoopRef都代表著RunLoop對象,它們是等價的炼杖,可以互相轉(zhuǎn)換
- NSRunLoop是基于CFRunLoopRef的一層OC包裝,所以要了解RunLoop內(nèi)部結(jié)構(gòu)盗迟,需要多研究CFRunLoopRef層面的API(Core Foundation層面)
- 在iOS開發(fā)中有兩套api來訪問Runloop
RunLoop與線程關(guān)系
RunLoop與子線程關(guān)系:一個RunLoop對應(yīng)唯一的一個線程
RunLoop生命周期:在第一次獲取時創(chuàng)建坤邪,在相對應(yīng)線程死亡的時候銷毀
RunLoop的創(chuàng)建:主線程已經(jīng)創(chuàng)建好,子線程需要手動創(chuàng)建
-
如何獲得RunLoop對象:
- 獲得當前RunLoop對象:
//01 NSRunloop NSRunLoop * runloop1 = [NSRunLoop currentRunLoop]; //02 CFRunLoopRef CFRunLoopRef runloop2 = CFRunLoopGetCurrent();
- 拿到當前對應(yīng)程序的主線程
//01 NSRunloop NSRunLoop * runloop1 = [NSRunLoop mainRunLoop]; //02 CFRunLoopRef CFRunLoopRef runloop2 = CFRunLoopGetMain();
- 注意點:開一個子線程創(chuàng)建runloop,不是通過alloc init方法創(chuàng)建罚缕,而是直接通過調(diào)用currentRunLoop方法來創(chuàng)建艇纺,它本身是一個懶加載的。
- 在子線程中邮弹,如果不主動獲取Runloop的話黔衡,那么子線程內(nèi)部是不會創(chuàng)建Runloop的‰缦纾可以下載CFRunloopRef的源碼盟劫,搜索_CFRunloopGet0,查看代碼。
- Runloop對象是利用字典來進行存儲与纽,而且key是對應(yīng)的線程Value為該線程對應(yīng)的Runloop侣签。
- RunLoop相關(guān)類
- RunLoop運行原理圖
- 獲得當前RunLoop對象:
- RunLoop與相關(guān)類之間的關(guān)系圖
5)CFRunloopTimerRef
(1)NSTimer相關(guān)代碼
/*
說明:
(1)runloop一啟動就會選中一種模式塘装,當選中了一種模式之后其它的模式就都不鳥。一個mode里面可以添加多個NSTimer,也就是說以后當創(chuàng)建NSTimer的時候影所,可以指定它是在什么模式下運行的氢哮。
(2)它是基于時間的觸發(fā)器,說直白點那就是時間到了我就觸發(fā)一個事件型檀,觸發(fā)一個操作冗尤。基本上說的就是NSTimer
(3)相關(guān)代碼
*/
- (void)timer2
{
//NSTimer 調(diào)用了scheduledTimer方法胀溺,那么會自動添加到當前的runloop里面去裂七,而且runloop的運行模式kCFRunLoopDefaultMode
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//更改模式
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)timer1
{
// [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//定時器添加到UITrackingRunLoopMode模式,一旦runloop切換模式仓坞,那么定時器就不工作
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
//定時器添加到NSDefaultRunLoopMode模式背零,一旦runloop切換模式,那么定時器就不工作
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
//占位模式:common modes標記
//被標記為common modes的模式 kCFRunLoopDefaultMode UITrackingRunLoopMode
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// NSLog(@"%@",[NSRunLoop currentRunLoop]);
}
- (void)run
{
NSLog(@"---run---%@",[NSRunLoop currentRunLoop].currentMode);
}
- (IBAction)btnClick {
NSLog(@"---btnClick---");
}
(2)GCD中的定時器
//0.創(chuàng)建一個隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//1.創(chuàng)建一個GCD的定時器
/*
第一個參數(shù):說明這是一個定時器
第四個參數(shù):GCD的回調(diào)任務(wù)添加到那個隊列中執(zhí)行无埃,如果是主隊列則在主線程執(zhí)行
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//2.設(shè)置定時器的開始時間徙瓶,間隔時間以及精準度
//設(shè)置開始時間,三秒鐘之后調(diào)用
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW,3.0 *NSEC_PER_SEC);
//設(shè)置定時器工作的間隔時間
uint64_t intevel = 1.0 * NSEC_PER_SEC;
/*
第一個參數(shù):要給哪個定時器設(shè)置
第二個參數(shù):定時器的開始時間DISPATCH_TIME_NOW表示從當前開始
第三個參數(shù):定時器調(diào)用方法的間隔時間
第四個參數(shù):定時器的精準度嫉称,如果傳0則表示采用最精準的方式計算侦镇,如果傳大于0的數(shù)值,則表示該定時切換i可以接收該值范圍內(nèi)的誤差织阅,通常傳0
該參數(shù)的意義:可以適當?shù)奶岣叱绦虻男阅? 注意點:GCD定時器中的時間以納秒為單位(面試)
*/
dispatch_source_set_timer(timer, start, intevel, 0 * NSEC_PER_SEC);
//3.設(shè)置定時器開啟后回調(diào)的方法
/*
第一個參數(shù):要給哪個定時器設(shè)置
第二個參數(shù):回調(diào)block
*/
dispatch_source_set_event_handler(timer, ^{
NSLog(@"------%@",[NSThread currentThread]);
});
//4.執(zhí)行定時器
dispatch_resume(timer);
//注意:dispatch_source_t本質(zhì)上是OC類壳繁,在這里是個局部變量,需要強引用
self.timer = timer;
GCD定時器補充
/*
DISPATCH_SOURCE_TYPE_TIMER 定時響應(yīng)(定時器事件)
DISPATCH_SOURCE_TYPE_SIGNAL 接收到UNIX信號時響應(yīng)
DISPATCH_SOURCE_TYPE_READ IO操作荔棉,如對文件的操作闹炉、socket操作的讀響應(yīng)
DISPATCH_SOURCE_TYPE_WRITE IO操作,如對文件的操作润樱、socket操作的寫響應(yīng)
DISPATCH_SOURCE_TYPE_VNODE 文件狀態(tài)監(jiān)聽渣触,文件被刪除、移動壹若、重命名
DISPATCH_SOURCE_TYPE_PROC 進程監(jiān)聽,如進程的退出嗅钻、創(chuàng)建一個或更多的子線程、進程收到UNIX信號
下面兩個都屬于Mach相關(guān)事件響應(yīng)
DISPATCH_SOURCE_TYPE_MACH_SEND
DISPATCH_SOURCE_TYPE_MACH_RECV
下面兩個都屬于自定義的事件舌稀,并且也是有自己來觸發(fā)
DISPATCH_SOURCE_TYPE_DATA_ADD
DISPATCH_SOURCE_TYPE_DATA_OR
*/
6)CFRunloopSourceRef
(1)是事件源也就是輸入源啊犬,有兩種分類模式;
a.一種是按照蘋果官方文檔進行劃分的
b.另一種是基于函數(shù)的調(diào)用棧來進行劃分的(source0和source1)壁查。
(2)具體的分類情況
a.以前的分法
Port-Based Sources
Custom Input Sources
Cocoa Perform Selector Sources
b.現(xiàn)在的分法
Source0:非基于Port的
Source1:基于Port的
(3)可以通過打斷點的方式查看一個方法的函數(shù)調(diào)用棧
7)CFRunLoopObserverRef
(1)CFRunLoopObserverRef是觀察者觉至,能夠監(jiān)聽RunLoop的狀態(tài)改變
(2)如何監(jiān)聽
//創(chuàng)建一個runloop監(jiān)聽者
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@"監(jiān)聽runloop狀態(tài)改變---%zd",activity);
});
//為runloop添加一個監(jiān)聽者
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
CFRelease(observer);
(3)監(jiān)聽的狀態(tài)
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), //即將進入Runloop
kCFRunLoopBeforeTimers = (1UL << 1), //即將處理NSTimer
kCFRunLoopBeforeSources = (1UL << 2), //即將處理Sources
kCFRunLoopBeforeWaiting = (1UL << 5), //即將進入休眠
kCFRunLoopAfterWaiting = (1UL << 6), //剛從休眠中喚醒
kCFRunLoopExit = (1UL << 7), //即將退出runloop
kCFRunLoopAllActivities = 0x0FFFFFFFU //所有狀態(tài)改變
};
3)Runloop運行邏輯
4.Runloop應(yīng)用
1)NSTimer
2)ImageView顯示:控制方法在特定的模式下可用
3)PerformSelector
4)常駐線程:在子線程中開啟一個runloop
5)自動釋放池
第一次創(chuàng)建:進入runloop的時候
最后一次釋放:runloop退出的時候
其它創(chuàng)建和釋放:當runloop即將休眠的時候會把之前的自動釋放池釋放,然后重新創(chuàng)建一個新的釋放池