如作為一個開發(fā)者,有一個學習的氛圍跟一個交流圈子特別重要尊浪,這是一個我的iOS交流群:638302184,不管你是小白還是大牛歡迎入駐 封救,分享BAT,阿里面試題拇涤、面試經(jīng)驗,討論技術(shù)誉结, 大家一起交流學習成長鹅士!
群內(nèi)提供數(shù)據(jù)結(jié)構(gòu)與算法、底層進階惩坑、swift掉盅、逆向、整合面試題等免費資料
附上一份收集的各大廠面試題(附答案) ! 群文件直接獲取
各大廠面試題
推薦閱讀:iOS開發(fā)——BAT面試題合集(持續(xù)更新中)
Swift好多坑以舒,一個人填不來趾痘,怎么辦
問身邊同事吧,又怕被暗笑技術(shù)差勁
1蔓钟、如何追蹤app崩潰率永票,如何解決線上閃退
當iOS設(shè)備上的App應(yīng)用閃退時,操作系統(tǒng)會生成一個crash日志,保存在設(shè)備上侣集。crash日志上有很多有用的信息键俱,比如每個正在執(zhí)行線程的完整堆棧跟蹤信息和內(nèi)存映像,這樣就能夠通過解析這些信息進而定位crash發(fā)生時的代碼邏輯世分,從而找到App閃退的原因编振。通常來說,crash產(chǎn)生來源于兩種問題:違反iOS系統(tǒng)規(guī)則導(dǎo)致的crash和App代碼邏輯BUG導(dǎo)致的crash臭埋,下面分別對他們進行分析踪央。
1.1、違反iOS系統(tǒng)規(guī)則產(chǎn)生crash的三種類型
(1) 內(nèi)存報警閃退
當iOS檢測到內(nèi)存過低時斋泄,它的VM系統(tǒng)會發(fā)出低內(nèi)存警告通知杯瞻,嘗試回收一些內(nèi)存;如果情況沒有得到足夠的改善炫掐,iOS會終止后臺應(yīng)用以回收更多內(nèi)存;最后睬涧,如果內(nèi)存還是不足募胃,那么正在運行的應(yīng)用可能會被終止掉。在Debug模式下畦浓,可以主動將客戶端執(zhí)行的動作邏輯寫入一個log文件中痹束,這樣程序童鞋可以將內(nèi)存預(yù)警的邏輯寫入該log文件,當發(fā)生如下截圖中的內(nèi)存報警時讶请,就是提醒當前客戶端性能內(nèi)存吃緊祷嘶,可以通過Instruments工具中的Allocations 和 Leaks模塊庫來發(fā)現(xiàn)內(nèi)存分配問題和內(nèi)存泄漏問題。
(2) 響應(yīng)超時
當應(yīng)用程序?qū)σ恍┨囟ǖ氖录ū热鐔佣嵋纭炱鹇畚 ⒒謴?fù)、結(jié)束)響應(yīng)不及時风响,蘋果的Watchdog機制會把應(yīng)用程序干掉嘉汰,并生成一份相應(yīng)的crash日志。這些事件與下列UIApplicationDelegate方法相對應(yīng)状勤,當遇到Watchdog日志時鞋怀,可以檢查上圖中的幾個方法是否有比較重的阻塞UI的動作。
application:didFinishLaunchingWithOptions:
applicationWillResignActive:
applicationDidEnterBackground:
applicationWillEnterForeground:
applicationDidBecomeActive:
applicationWillTerminate:
(3) 用戶強制退出
一看到“用戶強制退出”持搜,首先可能想到的雙擊Home鍵密似,然后關(guān)閉應(yīng)用程序。不過這種場景一般是不會產(chǎn)生crash日志的葫盼,因為雙擊Home鍵后残腌,所有的應(yīng)用程序都處于后臺狀態(tài),而iOS隨時都有可能關(guān)閉后臺進程,當應(yīng)用阻塞界面并停止響應(yīng)時這種場景才會產(chǎn)生crash日志废累。這里指的“用戶強制退出”場景邓梅,是稍微比較復(fù)雜點的操作:先按住電源鍵,直到出現(xiàn)“滑動關(guān)機”的界面時邑滨,再按住Home鍵日缨,這時候當前應(yīng)用程序會被終止掉,并且產(chǎn)生一份相應(yīng)事件的crash日志掖看。
1.2匣距、應(yīng)用邏輯的Bug
大多數(shù)閃退崩潰日志的產(chǎn)生都是因為應(yīng)用中的Bug,這種Bug的錯誤種類有很多哎壳,比如
- SEGV:(Segmentation Violation毅待,段違例),無效內(nèi)存地址归榕,比如空指針尸红,未初始化指針,棧溢出等刹泄;
- SIGABRT:收到Abort信號外里,可能自身調(diào)用abort()或者收到外部發(fā)送過來的信號;
- SIGBUS:總線錯誤特石。與SIGSEGV不同的是盅蝗,SIGSEGV訪問的是無效地址(比如虛存映射不到物理內(nèi)存),而SIGBUS訪問的是有效地址姆蘸,但總線訪問異常(比如地址對齊問題)墩莫;
- SIGILL:嘗試執(zhí)行非法的指令,可能不被識別或者沒有權(quán)限逞敷;
- SIGFPE:Floating Point Error狂秦,數(shù)學計算相關(guān)問題(可能不限于浮點計算),比如除零操作兰粉;
- SIGPIPE:管道另一端沒有進程接手數(shù)據(jù)故痊;
常見的崩潰原因基本都是代碼邏輯問題或資源問題,比如數(shù)組越界玖姑,訪問野指針或者資源不存在愕秫,或資源大小寫錯誤等。
1.3焰络、crash的收集
如果是在windows上你可以通過itools或pp助手等輔助工具查看系統(tǒng)產(chǎn)生的歷史crash日志戴甩,然后再根據(jù)app來查看。如果是在Mac 系統(tǒng)上闪彼,只需要打開xcode->windows->devices甜孤,選擇device logs進行查看协饲,如下圖,這些crash文件都可以導(dǎo)出來缴川,然后再單獨對這個crash文件做處理分析茉稠。
<ignore_js_op>[圖片上傳中...(image-b1ea04-1554465565925-3)]</ignore_js_op>
看日志
市場上已有的商業(yè)軟件提供crash收集服務(wù),這些軟件基本都提供了日志存儲把夸,日志符號化解析和服務(wù)端可視化管理等服務(wù):
開源的軟件也可以拿來收集crash日志而线,比如Razor,QuincyKit(git鏈接)等,這些軟件收集crash的原理其實大同小異恋日,都是根據(jù)系統(tǒng)產(chǎn)生的crash日志進行了一次提取或封裝膀篮,然后將封裝后的crash文件上傳到對應(yīng)的服務(wù)端進行解析處理。很多商業(yè)軟件都采用了Plcrashreporter這個開源工具來上傳和解析crash岂膳,比如HockeyApp,Flurry和crittercism等誓竿。
<ignore_js_op>[圖片上傳中...(image-8de82e-1554465565925-2)]</ignore_js_op>
crash信息
由于自己的crash信息太長,找了一張示例:
1)crash標識是應(yīng)用進程產(chǎn)生crash時的一些標識信息谈截,它描述了該crash的唯一標識(E838FEFB-ECF6-498C-8B35-D40F0F9FEAE4)筷屡,所發(fā)生的硬件設(shè)備類型(iphone3,1代表iphone4),以及App進程相關(guān)的信息等簸喂;
2)基本信息描述的是crash發(fā)生的時間和系統(tǒng)版本速蕊;
3)異常類型描述的是crash發(fā)生時拋出的異常類型和錯誤碼;
4)線程回溯描述了crash發(fā)生時所有線程的回溯信息娘赴,每個線程在每一幀對應(yīng)的函數(shù)調(diào)用信息(這里由于空間限制沒有全部列出);
5)二進制映像是指crash發(fā)生時已加載的二進制文件跟啤。以上就是一份crash日志包含的所有信息诽表,接下來就需要根據(jù)這些信息去解析定位導(dǎo)致crash發(fā)生的代碼邏輯, 這就需要用到符號化解析的過程(洋名叫:symbolication)隅肥。
1.4竿奏、解決線上閃退
首先保證,發(fā)布前充分測試腥放。發(fā)布后依然有閃退現(xiàn)象泛啸,查看崩潰日志,及時修復(fù)并發(fā)布秃症。
2候址、什么是事件響應(yīng)鏈,點擊屏幕時是如何互動的种柑,事件的傳遞岗仑。
<ignore_js_op>[圖片上傳中...(image-7b6265-1554465565925-1)]</ignore_js_op>
事件響應(yīng)鏈
對于iOS設(shè)備用戶來說,他們操作設(shè)備的方式主要有三種:觸摸屏幕聚请、晃動設(shè)備荠雕、通過遙控設(shè)施控制設(shè)備。對應(yīng)的事件類型有以下三種:
1、觸屏事件(Touch Event)
2炸卑、運動事件(Motion Event)
3既鞠、遠端控制事件(Remote-Control Event)
2.1、響應(yīng)者鏈(Responder Chain)
響應(yīng)者對象(Responder Object)盖文,指的是有響應(yīng)和處理事件能力的對象嘱蛋。響應(yīng)者鏈就是由一系列的響應(yīng)者對象構(gòu)成的一個層次結(jié)構(gòu)。
UIResponder是所有響應(yīng)對象的基類椅寺,在UIResponder類中定義了處理上述各種事件的接口浑槽。我們熟悉的UIApplication、 UIViewController返帕、UIWindow和所有繼承自UIView的UIKit類都直接或間接的繼承自UIResponder桐玻,所以它們的實例都是可以構(gòu)成響應(yīng)者鏈的響應(yīng)者對象。
響應(yīng)者鏈有以下特點:
1荆萤、響應(yīng)者鏈通常是由視圖(UIView)構(gòu)成的镊靴;
2、一個視圖的下一個響應(yīng)者是它視圖控制器(UIViewController)(如果有的話)链韭,然后再轉(zhuǎn)給它的父視圖(Super View)偏竟;
3、視圖控制器(如果有的話)的下一個響應(yīng)者為其管理的視圖的父視圖敞峭;
4踊谋、單例的窗口(UIWindow)的內(nèi)容視圖將指向窗口本身作為它的下一個響應(yīng)者
需要指出的是,Cocoa Touch應(yīng)用不像Cocoa應(yīng)用旋讹,它只有一個UIWindow對象殖蚕,因此整個響應(yīng)者鏈要簡單一點;
5沉迹、單例的應(yīng)用(UIApplication)是一個響應(yīng)者鏈的終點睦疫,它的下一個響應(yīng)者指向nil,以結(jié)束整個循環(huán)鞭呕。
2.2蛤育、點擊屏幕時是如何互動的
iOS系統(tǒng)檢測到手指觸摸(Touch)操作時會將其打包成一個UIEvent對象,并放入當前活動Application的事件隊列葫松,單例的UIApplication會從事件隊列中取出觸摸事件并傳遞給單例的UIWindow來處理瓦糕,UIWindow對象首先會使用hitTest:withEvent:方法尋找此次Touch操作初始點所在的視圖(View),即需要將觸摸事件傳遞給其處理的視圖进宝,這個過程稱之為hit-test view刻坊。
UIWindow實例對象會首先在它的內(nèi)容視圖上調(diào)用hitTest:withEvent:,此方法會在其視圖層級結(jié)構(gòu)中的每個視圖上調(diào)用pointInside:withEvent:(該方法用來判斷點擊事件發(fā)生的位置是否處于當前視圖范圍內(nèi)党晋,以確定用戶是不是點擊了當前視圖)谭胚,如果pointInside:withEvent:返回YES徐块,則繼續(xù)逐級調(diào)用,直到找到touch操作發(fā)生的位置灾而,這個視圖也就是要找的hit-test view胡控。
hitTest:withEvent:方法的處理流程如下:首先調(diào)用當前視圖的pointInside:withEvent:方法判斷觸摸點是否在當前視圖內(nèi);若返回NO,則hitTest:withEvent:返回nil;若返回YES,則向當前視圖的所有子視圖(subviews)發(fā)送hitTest:withEvent:消息旁趟,所有子視圖的遍歷順序是從最頂層視圖一直到到最底層視圖昼激,即從subviews數(shù)組的末尾向前遍歷,直到有子視圖返回非空對象或者全部子視圖遍歷完畢锡搜;若第一次有子視圖返回非空對象橙困,則hitTest:withEvent:方法返回此對象,處理結(jié)束耕餐;如所有子視圖都返回非凡傅,則hitTest:withEvent:方法返回自身(self)。
事件的傳遞和響應(yīng)分兩個鏈:
傳遞鏈:由系統(tǒng)向離用戶最近的view傳遞肠缔。UIKit –> active app’s event queue –> window –> root view –>……–>lowest view
響應(yīng)鏈:由離用戶最近的view向系統(tǒng)傳遞夏跷。initial view –> super view –> …..–> view controller –> window –> Application
3、Run Loop是什么明未,使用的目的槽华,何時使用和關(guān)注點
Run Loop是一讓線程能隨時處理事件但不退出的機制。RunLoop 實際上是一個對象趟妥,這個對象管理了其需要處理的事件和消息猫态,并提供了一個入口函數(shù)來執(zhí)行Event Loop 的邏輯。線程執(zhí)行了這個函數(shù)后披摄,就會一直處于這個函數(shù)內(nèi)部 "接受消息->等待->處理" 的循環(huán)中懂鸵,直到這個循環(huán)結(jié)束(比如傳入 quit 的消息),函數(shù)返回行疏。讓線程在沒有處理消息時休眠以避免資源占用、在有消息到來時立刻被喚醒套像。
OSX/iOS 系統(tǒng)中酿联,提供了兩個這樣的對象:NSRunLoop 和 CFRunLoopRef。CFRunLoopRef 是在 CoreFoundation 框架內(nèi)的夺巩,它提供了純 C 函數(shù)的 API贞让,所有這些 API 都是線程安全的。NSRunLoop 是基于 CFRunLoopRef 的封裝柳譬,提供了面向?qū)ο蟮?API喳张,但是這些 API 不是線程安全的。
線程和 RunLoop 之間是一一對應(yīng)的美澳,其關(guān)系是保存在一個全局的 Dictionary 里销部。線程剛創(chuàng)建時并沒有 RunLoop摸航,如果你不主動獲取,那它一直都不會有舅桩。RunLoop 的創(chuàng)建是發(fā)生在第一次獲取時酱虎,RunLoop 的銷毀是發(fā)生在線程結(jié)束時阿弃。你只能在一個線程的內(nèi)部獲取其 RunLoop(主線程除外)斩例。
3.1、系統(tǒng)默認注冊了5個Mode:
- kCFRunLoopDefaultMode: App的默認 Mode秸苗,通常主線程是在這個 Mode 下運行的撒妈。
- UITrackingRunLoopMode: 界面跟蹤 Mode恢暖,用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響狰右。
- UIInitializationRunLoopMode: 在剛啟動 App 時第進入的第一個 Mode杰捂,啟動完成后就不再使用。
- GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode挟阻,通常用不到琼娘。
- kCFRunLoopCommonModes: 這是一個占位的 Mode,沒有實際作用附鸽。
3.2脱拼、Run Loop的四個作用:
- 使程序一直運行接受用戶輸入
- 決定程序在何時應(yīng)該處理哪些Event
- 調(diào)用解耦
- 節(jié)省CPU時間
主線程的run loop默認是啟動的。iOS的應(yīng)用程序里面坷备,程序啟動后會有一個如下的main() 函數(shù):
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([appDelegate class]));
}
}
重點是UIApplicationMain() 函數(shù)熄浓,這個方法會為main thread 設(shè)置一個NSRunLoop 對象,這就解釋了本文開始說的為什么我們的應(yīng)用可以在無人操作的時候休息省撑,需要讓它干活的時候又能立馬響應(yīng)赌蔑。
對其它線程來說,run loop默認是沒有啟動的竟秫,如果你需要更多的線程交互則可以手動配置和啟動娃惯,如果線程只是去執(zhí)行一個長時間的已確定的任務(wù)則不需要。在任何一個Cocoa程序的線程中肥败,都可以通過:
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
來獲取到當前線程的run loop趾浅。
一個run loop就是一個事件處理循環(huán),用來不停的監(jiān)聽和處理輸入事件并將其分配到對應(yīng)的目標上進行處理馒稍。
NSRunLoop是一種更加高明的消息處理模式皿哨,他就高明在對消息處理過程進行了更好的抽象和封裝,這樣才能是的你不用處理一些很瑣碎很低層次的具體消息的處理纽谒,在NSRunLoop中每一個消息就被打包在input source或者是timer source中了证膨。使用run loop可以使你的線程在有工作的時候工作,沒有工作的時候休眠鼓黔,這可以大大節(jié)省系統(tǒng)資源央勒。
<ignore_js_op>[圖片上傳中...(image-d3574b-1554465565925-0)]</ignore_js_op>
RunLoop
3.3不见、什么時候使用run loop
僅當在為你的程序創(chuàng)建輔助線程的時候,你才需要顯式運行一個run loop订歪。Run loop是程序主線程基礎(chǔ)設(shè)施的關(guān)鍵部分脖祈。所以,Cocoa和Carbon程序提供了代碼運行主程序的循環(huán)并自動啟動run loop刷晋。IOS程序中UIApplication的run方法(或Mac OS X中的NSApplication)作為程序啟動步驟的一部分盖高,它在程序正常啟動的時候就會啟動程序的主循環(huán)。類似的眼虱,RunApplicationEventLoop函數(shù)為Carbon程序啟動主循環(huán)喻奥。如果你使用xcode提供的模板創(chuàng)建你的程序,那你永遠不需要自己去顯式的調(diào)用這些例程捏悬。
對于輔助線程撞蚕,你需要判斷一個run loop是否是必須的。如果是必須的过牙,那么你要自己配置并啟動它甥厦。你不需要在任何情況下都去啟動一個線程的run loop。比如寇钉,你使用線程來處理一個預(yù)先定義的長時間運行的任務(wù)時刀疙,你應(yīng)該避免啟動run loop。Run loop在你要和線程有更多的交互時才需要扫倡,比如以下情況:
使用端口或自定義輸入源來和其他線程通信
使用線程的定時器
Cocoa中使用任何performSelector…的方法
使線程周期性工作
3.4谦秧、關(guān)注點
3.4.1、Cocoa中的NSRunLoop類并不是線程安全的
我們不能再一個線程中去操作另外一個線程的run loop對象撵溃,那很可能會造成意想不到的后果疚鲤。不過幸運的是CoreFundation中的不透明類CFRunLoopRef是線程安全的,而且兩種類型的run loop完全可以混合使用缘挑。Cocoa中的NSRunLoop類可以通過實例方法:
- (CFRunLoopRef)getCFRunLoop;
獲取對應(yīng)的CFRunLoopRef類集歇,來達到線程安全的目的。
3.4.2语淘、Run loop的管理并不完全是自動的鬼悠。
我們?nèi)员仨氃O(shè)計線程代碼以在適當?shù)臅r候啟動run loop并正確響應(yīng)輸入事件,當然前提是線程中需要用到run loop亏娜。而且,我們還需要使用while/for語句來驅(qū)動run loop能夠循環(huán)運行蹬挺,下面的代碼就成功驅(qū)動了一個run loop:
BOOL isRunning = NO;
do {
isRunning = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDatedistantFuture]];
} while (isRunning);
3.4.3维贺、Run loop同時也負責autorelease pool的創(chuàng)建和釋放
在使用手動的內(nèi)存管理方式的項目中,會經(jīng)常用到很多自動釋放的對象巴帮,如果這些對象不能夠被即時釋放掉溯泣,會造成內(nèi)存占用量急劇增大虐秋。Run loop就為我們做了這樣的工作,每當一個運行循環(huán)結(jié)束的時候垃沦,它都會釋放一次autorelease pool客给,同時pool中的所有自動釋放類型變量都會被釋放掉。
4肢簿、ARC和MRC
Objective-c中提供了兩種內(nèi)存管理機制MRC(MannulReference Counting)和ARC(Automatic Reference Counting)靶剑,分別提供對內(nèi)存的手動和自動管理,來滿足不同的需求池充。Xcode 4.1及其以前版本沒有ARC桩引。
在MRC的內(nèi)存管理模式下,與對變量的管理相關(guān)的方法有:retain,release和autorelease收夸。retain和release方法操作的是引用記數(shù)坑匠,當引用記數(shù)為零時,便自動釋放內(nèi)存卧惜。并且可以用NSAutoreleasePool對象厘灼,對加入自動釋放池(autorelease調(diào)用)的變量進行管理,當drain時回收內(nèi)存咽瓷。
(1) retain设凹,該方法的作用是將內(nèi)存數(shù)據(jù)的所有權(quán)附給另一指針變量,引用數(shù)加1忱详,即retainCount+= 1;
(2) release围来,該方法是釋放指針變量對內(nèi)存數(shù)據(jù)的所有權(quán),引用數(shù)減1匈睁,即retainCount-= 1;
(3) autorelease监透,該方法是將該對象內(nèi)存的管理放到autoreleasepool中。
在ARC中與內(nèi)存管理有關(guān)的標識符航唆,可以分為變量標識符和屬性標識符胀蛮,對于變量默認為__strong,而對于屬性默認為unsafe_unretained糯钙。也存在autoreleasepool粪狼。
其中assign/retain/copy與MRC下property的標識符意義相同,strong類似與retain,assign類似于unsafe_unretained任岸,strong/weak/unsafe_unretained與ARC下變量標識符意義相同再榄,只是一個用于屬性的標識,一個用于變量的標識(帶兩個下劃短線__)享潜。所列出的其他的標識符與MRC下意義相同困鸥。
5、線程和進程
進程剑按,是并發(fā)執(zhí)行的程序在執(zhí)行過程中分配和管理資源的基本單位疾就,是一個動態(tài)概念澜术,竟爭計算機系統(tǒng)資源的基本單位。每一個進程都有一個自己的地址空間猬腰,即進程空間或(虛空間)鸟废。進程空間的大小 只與處理機的位數(shù)有關(guān),一個 16 位長處理機的進程空間大小為 216 姑荷,而 32 位處理機的進程空間大小為 232 盒延。進程至少有 5 種基本狀態(tài),它們是:初始態(tài)厢拭,執(zhí)行態(tài)兰英,等待狀態(tài),就緒狀態(tài)供鸠,終止狀態(tài)畦贸。
線程,在網(wǎng)絡(luò)或多用戶環(huán)境下楞捂,一個服務(wù)器通常需要接收大量且不確定數(shù)量用戶的并發(fā)請求薄坏,為每一個請求都創(chuàng)建一個進程顯然是行不通的,——無論是從系統(tǒng)資源開銷方面或是響應(yīng)用戶請求的效率方面來看寨闹。因此胶坠,操作系統(tǒng)中線程的概念便被引進了。線程繁堡,是進程的一部分沈善,一個沒有線程的進程可以被看作是單線程的。線程有時又被稱為輕權(quán)進程或輕量級進程椭蹄,也是 CPU 調(diào)度的一個基本單位闻牡。
進程的執(zhí)行過程是線狀的,盡管中間會發(fā)生中斷或暫停绳矩,但該進程所擁有的資源只為該線狀執(zhí)行過程服務(wù)罩润。一旦發(fā)生進程上下文切換,這些資源都是要被保護起來的翼馆。這是進程宏觀上的執(zhí)行過程割以。而進程又可有單線程進程與多線程進程兩種。我們知道应媚,進程有 一個進程控制塊 PCB 严沥,相關(guān)程序段 和 該程序段對其進行操作的數(shù)據(jù)結(jié)構(gòu)集 這三部分,單線程進程的執(zhí)行過程在宏觀上是線性的中姜,微觀上也只有單一的執(zhí)行過程消玄;而多線程進程在宏觀上的執(zhí)行過程同樣為線性的,但微觀上卻可以有多個執(zhí)行操作(線程),如不同代碼片段以及相關(guān)的數(shù)據(jù)結(jié)構(gòu)集莱找。線程的改變只代表了 CPU 執(zhí)行過程的改變,而沒有發(fā)生進程所擁有的資源變化嗜桌。除了 CPU 之外奥溺,計算機內(nèi)的軟硬件資源的分配與線程無關(guān),線程只能共享它所屬進程的資源骨宠。與進程控制表和 PCB 相似浮定,每個線程也有自己的線程控制表 TCB ,而這個 TCB 中所保存的線程狀態(tài)信息則要比 PCB 表少得多层亿,這些信息主要是相關(guān)指針用堆棧(系統(tǒng)棧和用戶棧)桦卒,寄存器中的狀態(tài)數(shù)據(jù)。進程擁有一個完整的虛擬地址空間匿又,不依賴于線程而獨立存在方灾;反之,線程是進程的一部分碌更,沒有自己的地址空間裕偿,與進程內(nèi)的其他線程一起共享分配給該進程的所有資源。
線程可以有效地提高系統(tǒng)的執(zhí)行效率痛单,但并不是在所有計算機系統(tǒng)中都是適用的嘿棘,如某些很少做進程調(diào)度和切換的實時系統(tǒng)。使用線程的好處是有多個任務(wù)需要處理機處理時旭绒,減少處理機的切換時間鸟妙;而且,線程的創(chuàng)建和結(jié)束所需要的系統(tǒng)開銷也比進程的創(chuàng)建和結(jié)束要小得多挥吵。最適用使用線程的系統(tǒng)是多處理機系統(tǒng)和網(wǎng)絡(luò)系統(tǒng)或分布式系統(tǒng)重父。
6、平常常用的多線程處理方式及優(yōu)缺點
iOS有四種多線程編程的技術(shù)蔫劣,分別是:NSThread坪郭,Cocoa NSOperation,GCD(全稱:Grand Central Dispatch),pthread脉幢。
6.1歪沃、四種方式的優(yōu)缺點介紹:
1)NSThread優(yōu)點:NSThread 比其他兩個輕量級。缺點:需要自己管理線程的生命周期嫌松,線程同步沪曙。線程同步對數(shù)據(jù)的加鎖會有一定的系統(tǒng)開銷。
2)Cocoa NSOperation優(yōu)點:不需要關(guān)心線程管理萎羔, 數(shù)據(jù)同步的事情液走,可以把精力放在自己需要執(zhí)行的操作上。Cocoa operation相關(guān)的類是NSOperation, NSOperationQueue.NSOperation是個抽象類,使用它必須用它的子類,可以實現(xiàn)它或者使用它定義好的兩個子類: NSInvocationOperation和NSBlockOperation.創(chuàng)建NSOperation子類的對象缘眶,把對象添加到NSOperationQueue隊列里執(zhí)行嘱根。
3)GCD(全優(yōu)點):Grand Central dispatch(GCD)是Apple開發(fā)的一個多核編程的解決方案。在iOS4.0開始之后才能使用巷懈。GCD是一個替代NSThread, NSOperationQueue,NSInvocationOperation等技術(shù)的很高效強大的技術(shù)该抒。
4)pthread是一套通用的多線程API,適用于Linux\Windows\Unix,跨平臺顶燕,可移植凑保,使用C語言,生命周期需要程序員管理涌攻,iOS開發(fā)中使用很少欧引。
6.2、GCD線程死鎖
GCD 確實好用 恳谎,很強大芝此,相比NSOpretion 無法提供 取消任務(wù)的功能。
如此強大的工具用不好可能會出現(xiàn)線程死鎖惠爽。 如下代碼:
- (void)viewDidLoad{
[super viewDidLoad];
NSLog(@"=================4");
dispatch_sync(dispatch_get_main_queue(),
^{ NSLog(@"=================5"); });
NSLog(@"=================6");
}
6.3癌蓖、GCD Queue 分為三種:
1,The main queue :主隊列婚肆,主線程就是在個隊列中租副。
2,Global queues : 全局并發(fā)隊列较性。
3用僧,用戶隊列:是用函數(shù) dispatch_queue_create創(chuàng)建的自定義隊列
6.4、dispatch_sync 和 dispatch_async 區(qū)別:
dispatch_async(queue,block) async 異步隊列赞咙,dispatch_async
函數(shù)會立即返回, block會在后臺異步執(zhí)行责循。
dispatch_sync(queue,block) sync 同步隊列,dispatch_sync
函數(shù)不會立即返回攀操,及阻塞當前線程,等待 block同步執(zhí)行完成院仿。
6.5、分析上面代碼:
viewDidLoad 在主線程中速和, 及在dispatch_get_main_queue() 中歹垫,執(zhí)行到sync 時 向
dispatch_get_main_queue()插入 同步 threed。sync 會等到 后面block 執(zhí)行完成才返回颠放, sync 又再 dispatch_get_main_queue() 隊列中排惨,它是串行隊列,sync 是后加入的碰凶,前一個是主線程暮芭,所以 sync 想執(zhí)行 block 必須等待主線程執(zhí)行完成鹿驼,主線程等待 sync 返回,去執(zhí)行后續(xù)內(nèi)容辕宏。照成死鎖畜晰,sync 等待mainThread 執(zhí)行完成, mianThread 等待sync 函數(shù)返回瑞筐。下面例子:
- (void)viewDidLoad{
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"=================1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"=================2"); });
NSLog(@"=================3"); });
}
程序會完成執(zhí)行舷蟀,為什么不會出現(xiàn)死鎖。
首先: async 在主線程中 創(chuàng)建了一個異步線程 加入 全局并發(fā)隊列面哼,async 不會等待block 執(zhí)行完成,立即返回扫步,
1魔策,async 立即返回, viewDidLoad 執(zhí)行完畢河胎,及主線程執(zhí)行完畢闯袒。
2,同時游岳,全局并發(fā)隊列立即執(zhí)行異步 block 政敢, 打印 1, 當執(zhí)行到 sync 它會等待 block 執(zhí)行完成才返回胚迫, 及等待dispatch_get_main_queue() 隊列中的 mianThread 執(zhí)行完成喷户, 然后才開始調(diào)用block 。因為1 和 2 幾乎同時執(zhí)行访锻,因為2 在全局并發(fā)隊列上褪尝, 2 中執(zhí)行到sync 時 1 可能已經(jīng)執(zhí)行完成或 等了一會,mainThread 很快退出期犬, 2 等已執(zhí)行后繼續(xù)內(nèi)容河哑。如果阻塞了主線程,2 中的sync 就無法執(zhí)行啦龟虎,mainThread 永遠不會退出璃谨, sync 就永遠等待著。
7鲤妥、大量數(shù)據(jù)表的優(yōu)化方案
1.對查詢進行優(yōu)化佳吞,要盡量避免全表掃描,首先應(yīng)考慮在 where 及 order by 涉及的列上建立索引旭斥。
2.應(yīng)盡量避免在 where 子句中對字段進行 null 值判斷容达,否則將導(dǎo)致引擎放棄使用索引而進行全表掃描,如:
select id from t where num is null
最好不要給數(shù)據(jù)庫留NULL垂券,盡可能的使用 NOT NULL填充數(shù)據(jù)庫.
備注花盐、描述羡滑、評論之類的可以設(shè)置為 NULL,其他的算芯,最好不要使用NULL柒昏。
不要以為 NULL 不需要空間,比如:char(100) 型熙揍,在字段建立時职祷,空間就固定了, 不管是否插入值(NULL也包含在內(nèi))届囚,都是占用 100個字符的空間的有梆,如果是varchar這樣的變長字段, null 不占用空間意系。
可以在num上設(shè)置默認值0泥耀,確保表中num列沒有null值,然后這樣查詢:
select id from t where num=0
3.應(yīng)盡量避免在 where 子句中使用 != 或 <> 操作符蛔添,否則將引擎放棄使用索引而進行全表掃描痰催。
4.應(yīng)盡量避免在 where 子句中使用 or 來連接條件,如果一個字段有索引迎瞧,一個字段沒有索引夸溶,將導(dǎo)致引擎放棄使用索引而進行全表掃描,如:
select id from t where num=10 or Name='admin'
可以這樣查詢:
select id from t where num=10 union all select id from t where Name='admin'
5.in 和 not in 也要慎用凶硅,否則會導(dǎo)致全表掃描缝裁,如:
select id from t where num in (1,2,3)
對于連續(xù)的數(shù)值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
很多時候用 exists 代替 in 是一個好的選擇:
select num from a where num in (select num from b)
用下面的語句替換:
select num from a where exists (select 1 from b where num=a.num)
6.下面的查詢也將導(dǎo)致全表掃描:
select id from t where name like '%abc%'
若要提高效率足绅,可以考慮全文檢索压语。
7.如果在 where 子句中使用參數(shù),也會導(dǎo)致全表掃描编检。因為SQL只有在運行時才會解析局部變量胎食,但優(yōu)化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇允懂。然 而厕怜,如果在編譯時建立訪問計劃,變量的值還是未知的蕾总,因而無法作為索引選擇的輸入項粥航。如下面語句將進行全表掃描:
select id from t where num=@num
可以改為強制查詢使用索引:
select id from t with (index(索引名)) where num=@num
8.應(yīng)盡量避免在 where 子句中對字段進行表達式操作,這將導(dǎo)致引擎放棄使用索引而進行全表掃描生百。如:
select id from t where num/2=100
應(yīng)改為:
select id from t where num=100*2
9.應(yīng)盡量避免在where子句中對字段進行函數(shù)操作递雀,這將導(dǎo)致引擎放棄使用索引而進行全表掃描。如:
select id from t where substring(name,1,3)='abc' -–name以abc開頭的id
select id from t where datediff(day,createdate,'2015-11-30')=0 -–'2015-11-30' --生成的id
應(yīng)改為:
select id from t where name like'abc%'
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'
10.不要在 where 子句中的“=”左邊進行函數(shù)蚀浆、算術(shù)運算或其他表達式運算缀程,否則系統(tǒng)將可能無法正確使用索引搜吧。
11.在使用索引字段作為條件時,如果該索引是復(fù)合索引杨凑,那么必須使用到該索引中的第一個字段作為條件時才能保證系統(tǒng)使用該索引滤奈,否則該索引將不會被使用,并且應(yīng)盡可能的讓字段順序與索引順序相一致撩满。
12.不要寫一些沒有意義的查詢蜒程,如需要生成一個空表結(jié)構(gòu):
select col1,col2 into #t from t where1=0
這類代碼不會返回任何結(jié)果集,但是會消耗系統(tǒng)資源的伺帘,應(yīng)改成這樣:
create table #t(…)
13.Update 語句昭躺,如果只更改1、2個字段伪嫁,不要Update全部字段窍仰,否則頻繁調(diào)用會引起明顯的性能消耗,同時帶來大量日志礼殊。
14.對于多張大數(shù)據(jù)量(這里幾百條就算大了)的表JOIN,要先分頁再JOIN针史,否則邏輯讀會很高晶伦,性能很差。
15.select count(*) from table婚陪;這樣不帶任何條件的count會引起全表掃描,并且沒有任何業(yè)務(wù)意義,是一定要杜絕的。
16.索引并不是越多越好,索引固然可以提高相應(yīng)的 select 的效率,但同時也降低了 insert 及 update 的效率北发,因為 insert 或 update 時有可能會重建索引逼蒙,所以怎樣建索引需要慎重考慮驳棱,視具體情況而定。一個表的索引數(shù)最好不要超過6個,若太多則應(yīng)考慮一些不常使用到的列上建的索引是否有 必要。
17.應(yīng)盡可能的避免更新 clustered 索引數(shù)據(jù)列倘感,因為 clustered 索引數(shù)據(jù)列的順序就是表記錄的物理存儲順序,一旦該列值改變將導(dǎo)致整個表記錄的順序的調(diào)整,會耗費相當大的資源。若應(yīng)用系統(tǒng)需要頻繁更新 clustered 索引數(shù)據(jù)列凤巨,那么需要考慮是否應(yīng)將該索引建為 clustered 索引彰檬。
18.盡量使用數(shù)字型字段景图,若只含數(shù)值信息的字段盡量不要設(shè)計為字符型,這會降低查詢和連接的性能设塔,并會增加存儲開銷序六。這是因為引擎在處理查詢和連 接時會逐個比較字符串中每一個字符,而對于數(shù)字型而言只需要比較一次就夠了。
19.盡可能的使用 varchar/nvarchar 代替 char/nchar 矿酵,因為首先變長字段存儲空間小唬复,可以節(jié)省存儲空間,其次對于查詢來說全肮,在一個相對較小的字段內(nèi)搜索效率顯然要高些敞咧。
20.任何地方都不要使用
select * from t
用具體的字段列表代替“*”,不要返回用不到的任何字段辜腺。
21.盡量使用表變量來代替臨時表休建。如果表變量包含大量數(shù)據(jù),請注意索引非常有限(只有主鍵索引)哪自。
22.避免頻繁創(chuàng)建和刪除臨時表丰包,以減少系統(tǒng)表資源的消耗。臨時表并不是不可使用壤巷,適當?shù)厥褂盟鼈兛梢允鼓承├谈行б乇耄纾斝枰貜?fù)引用大型表或常用表中的某個數(shù)據(jù)集時胧华。但是寄症,對于一次性事件, 最好使用導(dǎo)出表矩动。
23.在新建臨時表時有巧,如果一次性插入數(shù)據(jù)量很大,那么可以使用 select into 代替 create table悲没,避免造成大量 log 篮迎,以提高速度;如果數(shù)據(jù)量不大示姿,為了緩和系統(tǒng)表的資源甜橱,應(yīng)先create table,然后insert栈戳。
24.如果使用到了臨時表岂傲,在存儲過程的最后務(wù)必將所有的臨時表顯式刪除,先 truncate table 子檀,然后 drop table 镊掖,這樣可以避免系統(tǒng)表的較長時間鎖定。
25.盡量避免使用游標褂痰,因為游標的效率較差亩进,如果游標操作的數(shù)據(jù)超過1萬行,那么就應(yīng)該考慮改寫缩歪。
26.使用基于游標的方法或臨時表方法之前镐侯,應(yīng)先尋找基于集的解決方案來解決問題,基于集的方法通常更有效。
27.與臨時表一樣苟翻,游標并不是不可使用韵卤。對小型數(shù)據(jù)集使用 FAST_FORWARD 游標通常要優(yōu)于其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數(shù)據(jù)時崇猫。在結(jié)果集中包括“合計”的例程通常要比使用游標執(zhí)行的速度快沈条。如果開發(fā)時 間允許,基于游標的方法和基于集的方法都可以嘗試一下诅炉,看哪一種方法的效果更好蜡歹。
28.在所有的存儲過程和觸發(fā)器的開始處設(shè)置 SET NOCOUNT ON ,在結(jié)束時設(shè)置 SET NOCOUNT OFF 涕烧。無需在執(zhí)行存儲過程和觸發(fā)器的每個語句后向客戶端發(fā)送 DONE_IN_PROC 消息月而。
29.盡量避免大事務(wù)操作,提高系統(tǒng)并發(fā)能力议纯。
30.盡量避免向客戶端返回大數(shù)據(jù)量父款,若數(shù)據(jù)量過大,應(yīng)該考慮相應(yīng)需求是否合理瞻凤。
實際案例分析:拆分大的 DELETE 或INSERT 語句憨攒,批量提交SQL語句
如果你需要在一個在線的網(wǎng)站上去執(zhí)行一個大的 DELETE 或 INSERT 查詢,你需要非常小心阀参,要避免你的操作讓你的整個網(wǎng)站停止相應(yīng)肝集。因為這兩個操作是會鎖表的,表一鎖住了蛛壳,別的操作都進不來了杏瞻。
Apache 會有很多的子進程或線程。所以衙荐,其工作起來相當有效率捞挥,而我們的服務(wù)器也不希望有太多的子進程,線程和數(shù)據(jù)庫鏈接赫模,這是極大的占服務(wù)器資源的事情树肃,尤其是內(nèi)存蒸矛。
如果你把你的表鎖上一段時間瀑罗,比如30秒鐘,那么對于一個有很高訪問量的站點來說雏掠,這30秒所積累的訪問進程/線程斩祭,數(shù)據(jù)庫鏈接,打開的文件數(shù)乡话,可能不僅僅會讓你的WEB服務(wù)崩潰摧玫,還可能會讓你的整臺服務(wù)器馬上掛了。
所以,如果你有一個大的處理,你一定把其拆分,使用 LIMIT oracle(rownum),sqlserver(top)條件是一個好的方法咕别。下面是一個mysql示例:
while(1){//每次只做1000條
mysql_query(“delete from logs where log_date <= ’2015-11-01’ limit 1000”);
if(mysql_affected_rows() == 0){//刪除完成船侧,退出!break皆怕;
}//每次暫停一段時間,釋放表讓其他進程/線程訪問。
usleep(50000)
}
8对竣、常用到的動畫庫
Facebook 開源動畫庫 Pop 的 GitHub 主頁:facebook/pop · GitHub,介紹:Playing with Pop (i)
Canvas 項目主頁:Canvas - Simplify iOS Development榜配,介紹:Animate in Xcode Without Code
拿 Canvas 來和 Pop 比其實不大合適否纬,雖然兩者都自稱「動畫庫」,但是「庫」這個詞的含義有所區(qū)別蛋褥。本質(zhì)上 Canvas 是一個「動畫合集」而 Pop 是一個「動畫引擎」临燃。
先說 Canvas。Canvas 的目的是「Animate in Xcode Without Code」壁拉。開發(fā)者可以通過在 Storyboard 中指定 User Defined Runtime Attributes 來實現(xiàn)一些 Canvas 中預(yù)設(shè)的動畫谬俄,也就是他網(wǎng)站上能看到的那些。但是除了更改動畫的 delay 和 duration 基本上不能調(diào)整其他的參數(shù)弃理。
Pop 就不一樣了溃论。如果說 Canvas 是對 Core Animation 的封裝,Pop 則是對 Core Animation(以及 UIDynamics)的再實現(xiàn)痘昌。
Pop 語法上和 Core Animation 相似钥勋,效果上則不像 Canvas 那么生硬(時間四等分,振幅硬編碼)辆苔。這使得對 Core Animation 有了解的程序員可以很輕松地把原來的「靜態(tài)動畫」轉(zhuǎn)換成「動態(tài)動畫」算灸。
同時 Pop 又往前多走了一步。既然動畫的本質(zhì)是根據(jù)時間函數(shù)來做插值驻啤,那么理論上任何一個對象的任何一個值都可以用來做插值菲驴,而不僅僅是 Core Animation 里定死的那一堆大小、位移骑冗、旋轉(zhuǎn)赊瞬、縮放等 animatable properties。
9贼涩、Restful架構(gòu)
REST是一種架構(gòu)風格巧涧,其核心是面向資源,REST專門針對網(wǎng)絡(luò)應(yīng)用設(shè)計和開發(fā)方式遥倦,以降低開發(fā)的復(fù)雜性谤绳,提高系統(tǒng)的可伸縮性。REST提出設(shè)計概念和準則為:
1.網(wǎng)絡(luò)上的所有事物都可以被抽象為資源(resource)
2.每一個資源都有唯一的資源標識(resource identifier),對資源的操作不會改變這些標識
3.所有的操作都是無狀態(tài)的
REST簡化開發(fā)缩筛,其架構(gòu)遵循CRUD原則消略,該原則告訴我們對于資源(包括網(wǎng)絡(luò)資源)只需要四種行為:創(chuàng)建,獲取瞎抛,更新和刪除就可以完成相關(guān)的操作和處理疑俭。您可以通過統(tǒng)一資源標識符(Universal Resource Identifier,URI)來識別和定位資源婿失,并且針對這些資源而執(zhí)行的操作是通過 HTTP 規(guī)范定義的钞艇。其核心操作只有GET,PUT,POST,DELETE。
由于REST強制所有的操作都必須是stateless的豪硅,這就沒有上下文的約束哩照,如果做分布式,集群都不需要考慮上下文和會話保持的問題懒浮。極大的提高系統(tǒng)的可伸縮性飘弧。
RESTful架構(gòu):
(1)每一個URI代表一種資源;
(2)客戶端和服務(wù)器之間砚著,傳遞這種資源的某種表現(xiàn)層次伶;
(3)客戶端通過四個HTTP動詞,對服務(wù)器端資源進行操作稽穆,實現(xiàn)"表現(xiàn)層狀態(tài)轉(zhuǎn)化"冠王。
10、請分析下SDWebImage的原理
這個類庫提供一個UIImageView類別以支持加載來自網(wǎng)絡(luò)的遠程圖片舌镶。具有緩存管理柱彻、異步下載、同一個URL下載次數(shù)控制和優(yōu)化等特征餐胀。
10.1哟楷、SDWebImage 加載圖片的流程
1.入口 setImageWithURL:placeholderImage : options: 會先把 placeholderImage 顯示,然后 SDWebImageManager 根據(jù) URL 開始處理圖片否灾。
2.進入 SDWebImageManager-downloadWithURL:delegate: options:userInfo:卖擅,交給 SDImageCache 從緩存查找圖片是否已經(jīng)下載 queryDiskCacheForKey: delegate:userInfo:.
3.先從內(nèi)存圖片緩存查找是否有圖片,如果內(nèi)存中已經(jīng)有圖片緩存墨技,SDImageCacheDelegate 回調(diào) imageCache:didFindImage: forKey:userInfo: 到 SDWebImageManager惩阶。
4.SDWebImageManagerDelegate 回調(diào) webImageManager: didFinishWithImage: 到 UIImageView+WebCache 等前端展示圖片。
5.如果內(nèi)存緩存中沒有健提,生成 NSInvocationOperation 添加到隊列開始從硬盤查找圖片是否已經(jīng)緩存琳猫。
6.根據(jù) URLKey 在硬盤緩存目錄下嘗試讀取圖片文件伟叛。這一步是在 NSOperation 進行的操作私痹,所以回主線程進行結(jié)果回調(diào) notifyDelegate:。
7.如果上一操作從硬盤讀取到了圖片,將圖片添加到內(nèi)存緩存中(如果空閑內(nèi)存過小紊遵,會先清空內(nèi)存緩存)账千。SDImageCacheDelegate 回調(diào) imageCache: didFindImage: forKey: userInfo:。進而回調(diào)展示圖片暗膜。
8.如果從硬盤緩存目錄讀取不到圖片匀奏,說明所有緩存都不存在該圖片,需要下載圖片学搜,回調(diào) imageCache: didNotFindImageForKey: userInfo:娃善。
9.共享或重新生成一個下載器 SDWebImageDownloader 開始下載圖片。
10.圖片下載由 NSURLConnection 來做瑞佩,實現(xiàn)相關(guān) delegate 來判斷圖片下載中聚磺、下載完成和下載失敗。
11.connection: didReceiveData: 中利用 ImageIO 做了按圖片下載進度加載效果炬丸。
12.connectionDidFinishLoading: 數(shù)據(jù)下載完成后交給 SDWebImageDecoder 做圖片解碼處理瘫寝。
13.圖片解碼處理在一個 NSOperationQueue 完成,不會拖慢主線程 UI稠炬。如果有需要對下載的圖片進行二次處理焕阿,最好也在這里完成,效率會好很多首启。
14.在主線程 notifyDelegateOnMainThreadWithInfo: 宣告解碼完成暮屡,imageDecoder:didFinishDecodingImage: userInfo: 回調(diào)給 SDWebImageDownloader。
15.imageDownloader: didFinishWithImage: 回調(diào)給 SDWebImageManager 告知圖片下載完成毅桃。
16.通知所有的 downloadDelegates 下載完成栽惶,回調(diào)給需要的地方展示圖片。
17.將圖片保存到 SDImageCache 中疾嗅,內(nèi)存緩存和硬盤緩存同時保存外厂。寫文件到硬盤也在以單獨 NSInvocationOperation 完成,避免拖慢主線程代承。
18.SDImageCache 在初始化的時候會注冊一些消息通知汁蝶,在內(nèi)存警告或退到后臺的時候清理內(nèi)存圖片緩存,應(yīng)用結(jié)束的時候清理過期圖片论悴。
19.SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache掖棉,方便使用。
20.SDWebImagePrefetcher 可以預(yù)先下載圖片膀估,方便后續(xù)使用幔亥。
10.2、SDWebImage庫的作用
通過對UIImageView的類別擴展來實現(xiàn)異步加載替換圖片的工作察纯。
主要用到的對象:
1帕棉、UIImageView (WebCache)類別针肥,入口封裝,實現(xiàn)讀取圖片完成后的回調(diào)
2香伴、SDWebImageManager慰枕,對圖片進行管理的中轉(zhuǎn)站,記錄那些圖片正在讀取即纲。
向下層讀取Cache(調(diào)用SDImageCache)具帮,或者向網(wǎng)絡(luò)讀取對象(調(diào)用SDWebImageDownloader) 。
實現(xiàn)SDImageCache和SDWebImageDownloader的回調(diào)低斋。
3蜂厅、SDImageCache,根據(jù)URL的MD5摘要對圖片進行存儲和讀炔渤搿(實現(xiàn)存在內(nèi)存中或者存在硬盤上兩種實現(xiàn))
實現(xiàn)圖片和內(nèi)存清理工作葛峻。
4、SDWebImageDownloader巴比,根據(jù)URL向網(wǎng)絡(luò)讀取數(shù)據(jù)(實現(xiàn)部分讀取和全部讀取后再通知回調(diào)兩種方式)
其他類:
SDWebImageDecoder术奖,異步對圖像進行了一次解壓??
1、SDImageCache是怎么做數(shù)據(jù)管理的轻绞?
SDImageCache分兩個部分采记,一個是內(nèi)存層面的,一個是硬盤層面的政勃。內(nèi)存層面的相當是個緩存器唧龄,以Key-Value的形式存儲圖片。當內(nèi)存不夠的時候會清除所有緩存圖片奸远。用搜索文件系統(tǒng)的方式做管理既棺,文件替換方式是以時間為單位,剔除時間大于一周的圖片文件懒叛。當SDWebImageManager向SDImageCache要資源時丸冕,先搜索內(nèi)存層面的數(shù)據(jù),如果有直接返回薛窥,沒有的話去訪問磁盤胖烛,將圖片從磁盤讀取出來,然后做Decoder诅迷,將圖片對象放到內(nèi)存層面做備份佩番,再返回調(diào)用層。
2罢杉、為啥必須做Decoder趟畏?
由于UIImage的imageWithData函數(shù)是每次畫圖的時候才將Data解壓成ARGB的圖像,所以在每次畫圖的時候滩租,會有一個解壓操作赋秀,這樣效率很低利朵,但是只有瞬時的內(nèi)存需求。為了提高效率通過SDWebImageDecoder將包裝在Data下的資源解壓沃琅,然后畫在另外一張圖片上,這樣這張新圖片就不再需要重復(fù)解壓了蜘欲。
這種做法是典型的空間換時間的做法益眉。
文章來源網(wǎng)絡(luò)