SDWebImage框架詳解
- 下載圖片并顯示:
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:appM.icon] placeholderImage:[UIImage imageNamed:@"Snip20200808_172"]];
- 下載圖片/顯示圖片/內存緩存/磁盤緩存
-(void)download1
{
/*
第一個參數:要下載圖片的URL
第二個參數:占位圖片
第三個參數:下載選項
第四個參數:progress 進度回調
receivedSize:已經下載的數據大小
expectedSize:圖片的中大小
第五個參數:completed 完成回調(成功|失敗)
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) {
}];
}
- 下載圖片\內存緩存\磁盤緩存
-(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;
}
}];
}
- 下載圖片(完成后回調是在子線程中完成處理的)
-(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(), ^{
//設置圖片
self.imageView.image = image;
});
}];
}
- 顯示gif動畫
self.imageView.image = [UIImage sd_animatedGIFNamed:@"1234"];
- 接受到系統內存警告時如何處理:
//(1)取消當前正在進行的所有下載操作 [[SDWebImageManager sharedManager] cancelAll]; //(2)清除緩存數據 //cleanDisk:刪除過期的文件數據,計算當前未過期的已經下載的文件數據的大小,如果發現該數據大小大于我們設置的最大緩存數據大小,那么程序內部會按照按文件數據緩存的時間從遠到近刪除,知道小于最大緩存數據為止。 //clearMemory:直接刪除文件,重新創建新的文件夾 //[[SDWebImageManager sharedManager].imageCache cleanDisk]; [[SDWebImageManager sharedManager].imageCache clearMemory];
- SDWebImage內部實現細節:
- 判斷圖片當前類型:之判斷二進制數據的第一個字節
- 默認緩存周期:一周
- 緩存策略:默認進行內存和磁盤緩存,下載時首先檢查內存緩存,其次是磁盤緩存
- 緩存實現方式:采用了蘋果推出的用來處理緩存的NSCache
- 對內存警告的處理:框架內部監聽系統內存警告的通知,當發生時,自動移除緩存中的所有對象
- 下載隊列中對多個圖片任務采取的措施:方式有FIFO以及LIFO兩種方式,默認是FIFO
- 框架內允許的最大并發數6
- 磁盤緩存圖片的命名:對圖片url進行md5散列加密(【echo -n "url" |MD5】)
NSCache詳解
- NSCache簡單說明:
- NSCache是蘋果用來管理內存的類,類似于MutibleArray,在SDWebImage和AFN等框架中廣泛用來管理緩存
- NSCache在內存過低時會自動釋放對象(我們要手動釋放對象)
- NSCache是線程安全的,在使用過程中不需要加鎖
- NSCache的Key只是對對象進行Strong引用,不是拷貝,在清理的時候計算的是實際大小而不是引用的大小(不明白)
- NSCache屬性以及方法介紹
- 1)屬性介紹
- name:名稱
- delegete:設置代理
- totalCostLimit:緩存空間的最大總成本,超出上限會自動回收對象。默認值為0,表示沒有限制
- countLimit:能夠緩存的對象的最大數量。默認值為0,表示沒有限制
- evictsObjectsWithDiscardedContent:標識緩存是否回收廢棄的內容
- 2)方法介紹
objc - (void)setObject:(ObjectType)obj forKey:(KeyType)key;//在緩存中設置指定鍵名對應的值,0成本 - (void)setObject:(ObjectType)obj forKey:(KeyType)keycost:(NSUInteger)g; //在緩存中設置指定鍵名對應的值,并且指定該鍵值對的成本,用于計算記錄在緩存中的所有對象的總成本 //當出現內存警告或者超出緩存總成本上限的時候,緩存會開啟一個回收過程,刪除部分元素 - (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,
};
- 位移枚舉相關說明
- 特點:通過使用位移枚舉可以實現一個參數實現傳遞多個操作
- 原理:按位與只要有0則為0,按位或只要有1則為1
- 技巧:如果位移枚舉的第一個選項為0,那么在傳遞參數的時候默認可以傳0,傳0性能最優,不做額外的操作
RunLoop介紹:
基礎知識:
基本作用:
保證程序不退出(死循環)
處理各種事件(觸摸事件、定時器事件、selector事件等)
節省cpu資源,提高程序性能,該運行時運行,該休息時休息
-
RunLoop對象
- 在iOS開發中有兩套api來訪問Runloop
foundation框架【NSRunloop】
core foundation框架【CFRunloopRef】 - NSRunLoop和CFRunLoopRef都代表著RunLoop對象,它們是等價的,可以互相轉換
- NSRunLoop是基于CFRunLoopRef的一層OC包裝,所以要了解RunLoop內部結構,需要多研究CFRunLoopRef層面的API(Core Foundation層面)
- 在iOS開發中有兩套api來訪問Runloop
RunLoop與線程關系
RunLoop與子線程關系:一個RunLoop對應唯一的一個線程
RunLoop生命周期:在第一次獲取時創建,在相對應線程死亡的時候銷毀
RunLoop的創建:主線程已經創建好,子線程需要手動創建
-
如何獲得RunLoop對象:
- 獲得當前RunLoop對象:
//01 NSRunloop NSRunLoop * runloop1 = [NSRunLoop currentRunLoop]; //02 CFRunLoopRef CFRunLoopRef runloop2 = CFRunLoopGetCurrent();
- 拿到當前對應程序的主線程
//01 NSRunloop NSRunLoop * runloop1 = [NSRunLoop mainRunLoop]; //02 CFRunLoopRef CFRunLoopRef runloop2 = CFRunLoopGetMain();
- 注意點:開一個子線程創建runloop,不是通過alloc init方法創建,而是直接通過調用currentRunLoop方法來創建,它本身是一個懶加載的。
- 在子線程中,如果不主動獲取Runloop的話,那么子線程內部是不會創建Runloop的。可以下載CFRunloopRef的源碼,搜索_CFRunloopGet0,查看代碼。
- Runloop對象是利用字典來進行存儲,而且key是對應的線程Value為該線程對應的Runloop。
- RunLoop相關類
- RunLoop運行原理圖
- 獲得當前RunLoop對象:
2.png
- RunLoop與相關類之間的關系圖
1.png
5)CFRunloopTimerRef
(1)NSTimer相關代碼
/*
說明:
(1)runloop一啟動就會選中一種模式,當選中了一種模式之后其它的模式就都不鳥。一個mode里面可以添加多個NSTimer,也就是說以后當創建NSTimer的時候,可以指定它是在什么模式下運行的。
(2)它是基于時間的觸發器,說直白點那就是時間到了我就觸發一個事件,觸發一個操作。基本上說的就是NSTimer
(3)相關代碼
*/
- (void)timer2
{
//NSTimer 調用了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.創建一個隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//1.創建一個GCD的定時器
/*
第一個參數:說明這是一個定時器
第四個參數:GCD的回調任務添加到那個隊列中執行,如果是主隊列則在主線程執行
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//2.設置定時器的開始時間,間隔時間以及精準度
//設置開始時間,三秒鐘之后調用
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW,3.0 *NSEC_PER_SEC);
//設置定時器工作的間隔時間
uint64_t intevel = 1.0 * NSEC_PER_SEC;
/*
第一個參數:要給哪個定時器設置
第二個參數:定時器的開始時間DISPATCH_TIME_NOW表示從當前開始
第三個參數:定時器調用方法的間隔時間
第四個參數:定時器的精準度,如果傳0則表示采用最精準的方式計算,如果傳大于0的數值,則表示該定時切換i可以接收該值范圍內的誤差,通常傳0
該參數的意義:可以適當的提高程序的性能
注意點:GCD定時器中的時間以納秒為單位(面試)
*/
dispatch_source_set_timer(timer, start, intevel, 0 * NSEC_PER_SEC);
//3.設置定時器開啟后回調的方法
/*
第一個參數:要給哪個定時器設置
第二個參數:回調block
*/
dispatch_source_set_event_handler(timer, ^{
NSLog(@"------%@",[NSThread currentThread]);
});
//4.執行定時器
dispatch_resume(timer);
//注意:dispatch_source_t本質上是OC類,在這里是個局部變量,需要強引用
self.timer = timer;
GCD定時器補充
/*
DISPATCH_SOURCE_TYPE_TIMER 定時響應(定時器事件)
DISPATCH_SOURCE_TYPE_SIGNAL 接收到UNIX信號時響應
DISPATCH_SOURCE_TYPE_READ IO操作,如對文件的操作、socket操作的讀響應
DISPATCH_SOURCE_TYPE_WRITE IO操作,如對文件的操作、socket操作的寫響應
DISPATCH_SOURCE_TYPE_VNODE 文件狀態監聽,文件被刪除、移動、重命名
DISPATCH_SOURCE_TYPE_PROC 進程監聽,如進程的退出、創建一個或更多的子線程、進程收到UNIX信號
下面兩個都屬于Mach相關事件響應
DISPATCH_SOURCE_TYPE_MACH_SEND
DISPATCH_SOURCE_TYPE_MACH_RECV
下面兩個都屬于自定義的事件,并且也是有自己來觸發
DISPATCH_SOURCE_TYPE_DATA_ADD
DISPATCH_SOURCE_TYPE_DATA_OR
*/
6)CFRunloopSourceRef
(1)是事件源也就是輸入源,有兩種分類模式;
a.一種是按照蘋果官方文檔進行劃分的
b.另一種是基于函數的調用棧來進行劃分的(source0和source1)。
(2)具體的分類情況
a.以前的分法
Port-Based Sources
Custom Input Sources
Cocoa Perform Selector Sources
b.現在的分法
Source0:非基于Port的
Source1:基于Port的
(3)可以通過打斷點的方式查看一個方法的函數調用棧
7)CFRunLoopObserverRef
(1)CFRunLoopObserverRef是觀察者,能夠監聽RunLoop的狀態改變
(2)如何監聽
//創建一個runloop監聽者
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@"監聽runloop狀態改變---%zd",activity);
});
//為runloop添加一個監聽者
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
CFRelease(observer);
(3)監聽的狀態
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 //所有狀態改變
};
3)Runloop運行邏輯
3.png
4.png
4.Runloop應用
1)NSTimer
2)ImageView顯示:控制方法在特定的模式下可用
3)PerformSelector
4)常駐線程:在子線程中開啟一個runloop
5)自動釋放池
第一次創建:進入runloop的時候
最后一次釋放:runloop退出的時候
其它創建和釋放:當runloop即將休眠的時候會把之前的自動釋放池釋放,然后重新創建一個新的釋放池