當iOS設備上的App應用閃退時。操做系統會生成一個crash日誌。保存在設備上。crash日誌上有很是多實用的信息,比方每一個正在運行線程的完整堆棧跟蹤信息和內存映像,這樣就可以經過解析這些信息進而定位crash發生時的代碼邏輯,從而找到App閃退的緣由。css
一般來講,crash產生來源於兩種問題:違反iOS系統規則致使的crash和App代碼邏輯BUG致使的crash,如下分別對他們進行分析。html
違反iOS系統規則產生crash的三種類型前端
(1) 內存報警閃退
當iOS檢測到內存太低時,它的VM系統會發出低內存警告通知,嘗試回收一些內存;假設狀況沒有獲得足夠的改善,iOS會終止後臺應用以回收不少其它內存;最後。假設內存仍是不足,那麼正在運行的應用可能會被終止掉。在Debug模式下,可以主動將client運行的動做邏輯寫入一個log文件裏,這樣程序童鞋可以將內存預警的邏輯寫入該log文件,當發生例如如下截圖中的內存報警時,就是提醒當前client性能內存吃緊,可以經過Instruments工具中的Allocations 和 Leaks模塊庫來發現內存分配問題和內存泄漏問題。mysql
(2) 響應超時
當應用程序對一些特定的事件(比方啓動、掛起、恢復、結束)響應不及時。蘋果的Watchdog機制會把應用程序幹掉。並生成一份相應的crash日誌。git
這些事件與下列UIApplicationDelegate方法相相應。當遇到Watchdog日誌時,可以檢查上圖中的幾個方法是否有比較重的堵塞UI的動做。 github
application:didFinishLaunchingWithOptions: applicationWillResignActive: applicationDidEnterBackground: applicationWillEnterForeground: applicationDidBecomeActive: applicationWillTerminate:
(3) 用戶強制退出
一看到「用戶強制退出」,首先可能想到的雙擊Home鍵,而後關閉應用程序。web
只是這樣的場景一般是不會產生crash日誌的。因爲雙擊Home鍵後,所有的應用程序都處於後臺狀態,而iOS隨時都有可能關閉後臺進程,當應用堵塞界面並中止響應時這樣的場景纔會產生crash日誌。這裏指的「用戶強制退出」場景。是略微比較複雜點的操做:先按住電源鍵,直到出現「滑動關機」的界面時。再按住Home鍵,這時候當前應用程序會被終止掉,並且產生一份相應事件的crash日誌。sql
應用邏輯的Bug
大多數閃退崩潰日誌的產生都是因爲應用中的Bug,這樣的Bug的錯誤種類有很是多,比方 數據庫
SEGV:(Segmentation Violation,段違例),無效內存地址,比方空指針。未初始化指針,棧溢出等; SIGABRT:收到Abort信號,可能自身調用abort()或者收到外部發送過來的信號; SIGBUS:總線錯誤。與SIGSEGV不一樣的是,SIGSEGV訪問的是無效地址(比方虛存映射不到物理內存),而SIGBUS訪問的是有效地址,但總線訪問異常(比方地址對齊問題)。 SIGILL:嘗試運行非法的指令,可能不被識別或者沒有權限; SIGFPE:Floating Point Error,數學計算相關問題(可能不限於浮點計算),比方除零操做; SIGPIPE:管道還有一端沒有進程接手數據。
常見的崩潰緣由基本都是代碼邏輯問題或資源問題,比方數組越界。訪問野指針或者資源不存在。或資源大寫和小寫錯誤等。編程
crash的收集
假設是在windows上你可以經過itools或pp助手等輔助工具查看系統產生的歷史crash日誌,而後再依據app來查看。假設是在Mac 系統上。僅僅需要打開xcode->windows->devices。選擇device logs進行查看,例如如下圖,這些crash文件都可以導出來,而後再單獨對這個crash文件作處理分析。
市場上已有的商業軟件提供crash收集服務,這些軟件基本都提供了日誌存儲,日誌符號化解析和服務端可視化管理等服務:
Crashlytics (www.crashlytics.com)
Crittercism (www.crittercism.com)
Bugsense (www.bugsense.com)
HockeyApp (www.hockeyapp.net)
Flurry(www.flurry.com)
開源的軟件也可以拿來收集crash日誌,比方Razor,QuincyKit(git連接)等,這些軟件收集crash的原理事實上大同小異。都是依據系統產生的crash日誌進行了一次提取或封裝,而後將封裝後的crash文件上傳到相應的服務端進行解析處理。
很是多商業軟件都採用了Plcrashreporter這個開源工具來上傳和解析crash,比方HockeyApp,Flurry和crittercism等。
因爲本身的crash信息太長,找了一張演示樣例:
1)crash標識是應用進程產生crash時的一些標識信息。它描寫敘述了該crash的惟一標識(E838FEFB-ECF6-498C-8B35-D40F0F9FEAE4),所發生的硬件設備類型(iphone3,1表明iphone4)。以及App進程相關的信息等。
2)基本信息描寫敘述的是crash發生的時間和系統版本號。
3)異常類型描寫敘述的是crash發生時拋出的異常類型和錯誤碼;
4)線程回溯描寫敘述了crash發生時所有線程的回溯信息,每一個線程在每一幀相應的函數調用信息(這裏因爲空間限制沒有所有列出);
5)二進制映像是指crash發生時已載入的二進制文件。以上就是一份crash日誌包括的所有信息。接下來就需要依據這些信息去解析定位致使crash發生的代碼邏輯, 這就需要用到符號化解析的過程(洋名叫:symbolication)。
解決線上閃退
首先保證。公佈前充分測試。
公佈後依舊有閃退現象,查看崩潰日誌,及時修復並公佈。
對於IOS設備用戶來講,他們操做設備的方式主要有三種:觸摸屏幕、晃動設備、經過遙控設施控制設備。相應的事件類型有如下三種:
一、觸屏事件(Touch Event)
二、運動事件(Motion Event)
三、遠端控制事件(Remote-Control Event)
響應者鏈(Responder Chain)
響應者對象(Responder Object),指的是有響應和處理事件能力的對象。響應者鏈就是由一系列的響應者對象構成的一個層次結構。
UIResponder是所有響應對象的基類,在UIResponder類中定義了處理上述各類事件的接口。
咱們熟悉的UIApplication、 UIViewController、UIWindow和所有繼承自UIView的UIKit類都直接或間接的繼承自UIResponder。因此它們的實例都是可以構成響應者鏈的響應者對象。
響應者鏈有如下特色:
一、響應者鏈一般是由視圖(UIView)構成的;
二、一個視圖的下一個響應者是它視圖控制器(UIViewController)(假設有的話),而後再轉給它的父視圖(Super View)。
三、視圖控制器(假設有的話)的下一個響應者爲其管理的視圖的父視圖;
四、單例的窗體(UIWindow)的內容視圖將指向窗體自己做爲它的下一個響應者
需要指出的是,Cocoa Touch應用不像Cocoa應用,它僅僅有一個UIWindow對象,所以整個響應者鏈要簡單一點。
五、單例的應用(UIApplication)是一個響應者鏈的終點,它的下一個響應者指向nil,以結束整個循環。
點擊屏幕時是怎樣互動的
iOS系統檢測到手指觸摸(Touch)操做時會將其打包成一個UIEvent對象。並放入當前活動Application的事件隊列,單例的UIApplication會從事件隊列中取出觸摸事件並傳遞給單例的UIWindow來處理,UIWindow對象首先會使用hitTest:withEvent:方法尋找這次Touch操做初始點所在的視圖(View),即需要將觸摸事件傳遞給其處理的視圖。這個過程稱之爲hit-test view。
UIWindow實例對象會首先在它的內容視圖上調用hitTest:withEvent:。此方法會在其視圖層級結構中的每一個視圖上調用pointInside:withEvent:(該方法用來推斷點擊事件發生的位置是否處於當前視圖範圍內,以肯定用戶是否是點擊了當前視圖),假設pointInside:withEvent:返回YES。則繼續逐級調用。直到找到touch操做發生的位置。這個視圖也就是要找的hit-test view。
hitTest:withEvent:方法的處理流程例如如下:首先調用當前視圖的pointInside:withEvent:方法推斷觸摸點是否在當前視圖內;若返回NO,則hitTest:withEvent:返回nil;若返回YES,則向當前視圖的所有子視圖(subviews)發送hitTest:withEvent:消息。所有子視圖的遍歷順序是從最頂層視圖一直到到最底層視圖,即從subviews數組的末尾向前遍歷,直到有子視圖返回非空對象或者所有子視圖遍歷完畢;若第一次有子視圖返回非空對象,則hitTest:withEvent:方法返回此對象。處理結束。如所有子視圖都返回非。則hitTest:withEvent:方法返回自身(self)。
事件的傳遞和響應分兩個鏈:
傳遞鏈:由系統向離用戶近期的view傳遞。
UIKit –> active app’s event queue –> window –> root view –>……–>lowest view
響應鏈:由離用戶近期的view向系統傳遞。initial view –> super view –> …..–> view controller –> window –> Application
Run Loop是一讓線程能隨時處理事件但不退出的機制。
RunLoop 其實是一個對象。這個對象管理了其需要處理的事件和消息,並提供了一個入口函數來運行Event Loop 的邏輯。線程運行了這個函數後,就會一直處於這個函數內部 "接受消息->等待->處理" 的循環中,直到這個循環結束(比方傳入 quit 的消息),函數返回。讓線程在沒有處理消息時休眠以免資源佔用、在有消息到來時馬上被喚醒。
OSX/iOS 系統中。提供了兩個這樣的對象:NSRunLoop 和 CFRunLoopRef。
CFRunLoopRef 是在 CoreFoundation 框架內的,它提供了純 C 函數的 API,所有這些 API 都是線程安全的。
NSRunLoop 是基於 CFRunLoopRef 的封裝,提供了面向對象的 API,但是這些 API 不是線程安全的。
線程和 RunLoop 之間是一一相應的,其關係是保存在一個全局的 Dictionary 裏。
線程剛建立時並無 RunLoop,假設你不主動獲取,那它一直都不會有。
RunLoop 的建立是發生在第一次獲取時,RunLoop 的銷燬是發生在線程結束時。
你僅僅能在一個線程的內部獲取其 RunLoop(主線程除外)。
系統默認註冊了5個Mode:
Run Loop的四個做用:
使程序一直運行接受用戶輸入
決定程序在什麼時候應該處理哪些Event
調用解耦
節省CPU時間
主線程的run loop默認是啓動的。iOS的應用程序裏面。程序啓動後會有一個例如如下的main() 函數:
int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([appDelegate class])); } }
重點是UIApplicationMain() 函數,這種方法會爲main thread 設置一個NSRunLoop 對象,這就解釋了本文開始說的爲何咱們的應用可以在無人操做的時候歇息,需要讓它幹活的時候又能馬上響應。
對其它線程來講,run loop默認是沒有啓動的。假設你需要不少其它的線程交互則可以手動配置和啓動。假設線程僅僅是去運行一個長時間的已肯定的任務則不需要。在不論什麼一個Cocoa程序的線程中。都可以經過:
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
來獲取到當前線程的run loop。
一個run loop就是一個事件處理循環。用來不停的監聽和處理輸入事件並將其分配到相應的目標上進行處理。
NSRunLoop是一種更加高明的消息處理模式,他就高明在對消息處理過程進行了更好的抽象和封裝,這樣才幹是的你不用處理一些很是瑣碎很是低層次的詳細消息的處理。在NSRunLoop中每一個消息就被打包在input source或者是timer source中了。使用run loop可以使你的線程在有工做的時候工做。沒有工做的時候休眠,這可以大大節省系統資源。
何時使用run loop
僅當在爲你的程序建立輔助線程的時候。你才需要顯式運行一個run loop。
Run loop是程序主線程基礎設施的關鍵部分。因此,Cocoa和Carbon程序提供了代碼運行主程序的循環並本身主動啓動run loop。IOS程序中UIApplication的run方法(或Mac OS X中的NSApplication)做爲程序啓動步驟的一部分。它在程序正常啓動的時候就會啓動程序的主循環。
類似的,RunApplicationEventLoop函數爲Carbon程序啓動主循環。假設你使用xcode提供的模板建立你的程序,那你永遠不需要本身去顯式的調用這些例程。
對於輔助線程,你需要推斷一個run loop是不是必須的。假設是必須的。那麼你要本身配置並啓動它。你不需要在不論什麼狀況下都去啓動一個線程的run loop。
比方。你使用線程來處理一個預先定義的長時間運行的任務時,你應該避免啓動run loop。Run loop在你要和線程有不少其它的交互時才需要,比方如下狀況:
使用端口或本身定義輸入源來和其它線程通訊
使用線程的定時器
Cocoa中使用不論什麼performSelector…的方法
使線程週期性工做
關注點
Cocoa中的NSRunLoop類並不是線程安全的
咱們不能再一個線程中去操做另一個線程的run loop對象,那很是可能會形成意想不到的後果。只是幸運的是CoreFundation中的不透明類CFRunLoopRef是線程安全的,並且兩種類型的run loop全然可以混合使用。Cocoa中的NSRunLoop類可以經過實例方法:
- (CFRunLoopRef)getCFRunLoop;
獲取相應的CFRunLoopRef類,來達到線程安全的目的。
Run loop的管理並不全然是本身主動的。
咱們仍必須設計線程代碼以在適當的時候啓動run loop並正確響應輸入事件,固然前提是線程中需要用到run loop。並且,咱們還需要使用while/for語句來驅動run loop可以循環運行。如下的代碼就成功驅動了一個run loop:
BOOL isRunning = NO; do { isRunning = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDatedistantFuture]]; } while (isRunning);
Run loop同一時候也負責autorelease pool的建立和釋放
在使用手動的內存管理方式的項目中,會經常用到很是多本身主動釋放的對象,假設這些對象不可以被即時釋放掉,會形成內存佔用量急劇增大。
Run loop就爲咱們作了這樣的工做,每當一個運行循環結束的時候,它都會釋放一次autorelease pool,同一時候pool中的所有本身主動釋放類型變量都會被釋放掉。
Objective-c中提供了兩種內存管理機制MRC(MannulReference Counting)和ARC(Automatic Reference Counting),分別提供對內存的手動和本身主動管理。來知足不一樣的需求。
Xcode 4.1及其曾經版本號沒有ARC。
在MRC的內存管理模式下,與對變量的管理相關的方法有:retain,release和autorelease。
retain和release方法操做的是引用記數,當引用記數爲零時。便本身主動釋放內存。並且可以用NSAutoreleasePool對象,對加入本身主動釋放池(autorelease調用)的變量進行管理,當drain時回收內存。
(1) retain,該方法的做用是將內存數據的所有權附給還有一指針變量。引用數加1,即retainCount+= 1;
(2) release,該方法是釋放指針變量對內存數據的所有權,引用數減1,即retainCount-= 1;
(3) autorelease,該方法是將該對象內存的管理放到autoreleasepool中。
在ARC中與內存管理有關的標識符,可以分爲變量標識符和屬性標識符,對於變量默以爲__strong,而對於屬性默以爲unsafe_unretained。也存在autoreleasepool。
當中assign/retain/copy與MRC下property的標識符意義一樣,strong類似與retain,assign類似於unsafe_unretained,strong/weak/unsafe_unretained與ARC下變量標識符意義一樣。僅僅是一個用於屬性的標識,一個用於變量的標識(帶兩個下劃短線__)。所列出的其它的標識符與MRC下意義一樣。
進程。是併發運行的程序在運行過程當中分配和管理資源的基本單位,是一個動態概念,竟爭計算機系統資源的基本單位。每一個進程都有一個本身的地址空間,即進程空間或(虛空間)。進程空間的大小 僅僅與處理機的位數有關,一個 16 位好處理機的進程空間大小爲 216 ,而 32 位處理機的進程空間大小爲 232 。進程至少有 5 種基本狀態。它們是:初始態,運行態。等待狀態,就緒狀態,終止狀態。
線程,在網絡或多用戶環境下,一個server一般需要接收大量且不肯定數量用戶的併發請求,爲每一個請求都建立一個進程顯然是行不通的。——無論是從系統資源開銷方面或是響應用戶請求的效率方面來看。所以,操做系統中線程的概念便被引進了。
線程,是進程的一部分。一個沒有線程的進程可以被看做是單線程的。
線程有時又被稱爲輕權進程或輕量級進程,也是 CPU 調度的一個基本單位。
進程的運行過程是線狀的,雖然中間會發生中斷或暫停,但該進程所擁有的資源僅僅爲該線狀運行過程服務。一旦發生進程上下文切換,這些資源都是要被保護起來的。
這是進程宏觀上的運行過程。
而進程又可有單線程進程與多線程進程兩種。咱們知道,進程有 一個進程控制塊 PCB 。相關程序段 和 該程序段對其進行操做的數據結構集 這三部分,單線程進程的運行過程在宏觀上是線性的。微觀上也僅僅有單一的運行過程。而多線程進程在宏觀上的運行過程一樣爲線性的,但微觀上卻可以有多個運行操做(線程),如不一樣代碼片斷以及相關的數據結構集。線程的改變僅僅表明了 CPU 運行過程的改變。而沒有發生進程所擁有的資源變化。除了 CPU 以外,計算機內的軟硬件資源的分配與線程無關,線程僅僅能共享它所屬進程的資源。與進程控制表和 PCB 類似。每一個線程也有本身的線程控制表 TCB ,而這個 TCB 中所保存的線程狀態信息則要比 PCB 表少得多,這些信息主要是相關指針用堆棧(系統棧和用戶棧),寄存器中的狀態數據。進程擁有一個完整的虛擬地址空間,不依賴於線程而獨立存在;反之,線程是進程的一部分。沒有本身的地址空間。與進程內的其它線程一塊兒共享分配給該進程的所有資源。
線程可以有效地提升系統的運行效率,但並不是在所有計算機系統中都是適用的。如某些很是少作進程調度和切換的實時系統。使用線程的好處是有多個任務需要處理機處理時,減小處理機的切換時間;並且,線程的建立和結束所需要的系統開銷也比進程的建立和結束要小得多。最適用使用線程的系統是多處理機系統和網絡系統或分佈式系統。
iOS有四種多線程編程的技術。各自是:NSThread。Cocoa NSOperation,GCD(全稱:Grand Central Dispatch),pthread。
四種方式的優缺點介紹:
1)NSThread長處:NSThread 比其它兩個輕量級。缺點:需要本身管理線程的生命週期,線程同步。線程同步對數據的加鎖會有必定的系統開銷。
2)Cocoa NSOperation長處:不需要關心線程管理, 數據同步的事情,可以把精力放在本身需要運行的操做上。Cocoa operation相關的類是NSOperation, NSOperationQueue.NSOperation是個抽象類,使用它必須用它的子類。可以實現它或者使用它定義好的兩個子類: NSInvocationOperation和NSBlockOperation.建立NSOperation子類的對象,把對象加入到NSOperationQueue隊列裏運行。
3)GCD(全長處)Grand Central dispatch(GCD)是Apple開發的一個多核編程的解決方式。
在iOS4.0開始以後才幹使用。GCD是一個替代NSThread, NSOperationQueue,NSInvocationOperation等技術的很是高效強大的技術。
4) pthread是一套通用的多線程API,適用於Linux\Windows\Unix,跨平臺,可移植,使用C語言,生命週期需要程序猿管理,IOS開發中使用很是少。
GCD線程死鎖
GCD 確實好用 ,很是強大,相比NSOpretion 沒法提供 取消任務的功能。
如此強大的工具用很差可能會出現線程死鎖。 例如如下代碼:
- (void)viewDidLoad{ [super viewDidLoad]; NSLog(@"=================4"); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"=================5"); }); NSLog(@"=================6"); }
GCD Queue 分爲三種:
1。The main queue :主隊列。主線程就是在個隊列中。
2,Global queues : 全局併發隊列。
3。用戶隊列:是用函數 dispatch_queue_create建立的本身定義隊列
dispatch_sync 和 dispatch_async 差異:
dispatch_async(queue,block) async 異步隊列,dispatch_async
函數會立刻返回, block會在後臺異步運行。
dispatch_sync(queue,block) sync 同步隊列。dispatch_sync
函數不會立刻返回,及堵塞當前線程,等待 block同步運行完畢。
分析上面代碼:
viewDidLoad 在主線程中, 及在dispatch_get_main_queue() 中,運行到sync 時 向
dispatch_get_main_queue()插入 同步 threed。sync 會等到 後面block 運行完畢才返回, sync 又再 dispatch_get_main_queue() 隊列中。它是串行隊列。sync 是後加入的,前一個是主線程。因此 sync 想運行 block 必須等待主線程運行完畢,主線程等待 sync 返回。去運行興許內容。照成死鎖。sync 等待mainThread 運行完畢。 mianThread 等待sync 函數返回。如下樣例:
- (void)viewDidLoad{ [super viewDidLoad]; dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"=================1"); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"=================2"); }); NSLog(@"=================3"); }); }
程序會完畢運行。爲何不會出現死鎖。
首先: async 在主線程中 建立了一個異步線程 加入 全局併發隊列,async 不會等待block 運行完畢,立刻返回,
1,async 立刻返回, viewDidLoad 運行完畢。及主線程運行完畢。
2,同一時候。全局併發隊列立刻運行異步 block , 打印 1, 當運行到 sync 它會等待 block 運行完畢才返回。 及等待dispatch_get_main_queue() 隊列中的 mianThread 運行完畢, 而後才開始調用block 。
因爲1 和 2 差點兒同一時候運行,因爲2 在全局併發隊列上。 2 中運行到sync 時 1 可能已經運行完畢或 等了一會,mainThread 很是快退出。 2 等已運行後繼續內容。假設堵塞了主線程,2 中的sync 就沒法運行啦,mainThread 永遠不會退出。 sync 就永遠等待着。
1.對查詢進行優化,要儘可能避免全表掃描,首先應考慮在 where 及 order by 涉及的列上創建索引。
2.應儘可能避免在 where 子句中對字段進行 null 值推斷,不然將致使引擎放棄使用索引而進行全表掃描。如:
select id from t where num is null
最好不要給數據庫留NULL,儘量的使用 NOT NULL填充數據庫.
備註、描寫敘述、評論之類的可以設置爲 NULL。其它的。最好不要使用NULL。
不要覺得 NULL 不需要空間,比方:char(100) 型,在字段創建時,空間就固定了, 無論是否插入值(NULL也包括在內),都是佔用 100個字符的空間的,假設是varchar這樣的變長字段。 null 不佔用空間。
可以在num上設置默認值0,確保表中num列沒有null值。而後這樣查詢:
select id from t where num=0
3.應儘可能避免在 where 子句中使用 != 或 <> 操做符,不然將引擎放棄使用索引而進行全表掃描。
4.應儘可能避免在 where 子句中使用 or 來鏈接條件,假設一個字段有索引,一個字段沒有索引。將致使引擎放棄使用索引而進行全表掃描,如:
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 也要慎用,不然會致使全表掃描,如:
select id from t where num in (1,2,3)
對於連續的數值,能用 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.如下的查詢也將致使全表掃描:
select id from t where name like ‘%abc%’
若要提升效率。可以考慮全文檢索。
7.假設在 where 子句中使用參數。也會致使全表掃描。因爲SQL僅僅有在運行時纔會解析局部變量。但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然 而。假設在編譯時創建訪問計劃。變量的值仍是未知的,於是沒法做爲索引選擇的輸入項。
如如下語句將進行全表掃描:
select id from t where num=@num
可以改成強制查詢使用索引:
select id from t with (index(索引名)) where num=@num
應儘可能避免在 where 子句中對字段進行表達式操做,這將致使引擎放棄使用索引而進行全表掃描。
如:
select id from t where num/2=100
應改成:
select id from t where num=100*2
9.應儘可能避免在where子句中對字段進行函數操做,這將致使引擎放棄使用索引而進行全表掃描。如:
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
應改成:
select id from t where name like'abc%' select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'
10.不要在 where 子句中的「=」左邊進行函數、算術運算或其它表達式運算。不然系統將可能沒法正確使用索引。
11.在使用索引字段做爲條件時,假設該索引是複合索引,那麼必須使用到該索引中的第一個字段做爲條件時才幹保證系統使用該索引,不然該索引將不會被使用,並且應儘量的讓字段順序與索引順序相一致。
12.不要寫一些沒有意義的查詢,如需要生成一個空表結構:
select col1,col2 into #t from t where1=0
這類代碼不會返回不論什麼結果集。但是會消耗系統資源的。應改爲這樣:
create table #t(…)
13.Update 語句,假設僅僅更改一、2個字段。不要Update所有字段,不然頻繁調用會引發明顯的性能消耗。同一時候帶來大量日誌。
14.對於多張大數據量(這裏幾百條就算大了)的表JOIN,要先分頁再JOIN。不然邏輯讀會很是高,性能很是差。
15.select count(*) from table;這樣不帶不論什麼條件的count會引發全表掃描,並且沒有不論什麼業務意義。是必定要杜絕的。
16.索引並不是越多越好,索引當然可以提升相應的 select 的效率。但同一時候也減小了 insert 及 update 的效率,因爲 insert 或 update 時有可能會重建索引,因此怎樣建索引需要謹慎考慮,視詳細狀況而定。
一個表的索引數最好不要超過6個。若太多則應考慮一些不常使用到的列上建的索引是否有 必要。
17.應儘量的避免更新 clustered 索引數據列,因爲 clustered 索引數據列的順序就是表記錄的物理存儲順序。一旦該列值改變將致使整個表記錄的順序的調整,會耗費至關大的資源。若應用系統需要頻繁更新 clustered 索引數據列,那麼需要考慮是否應將該索引建爲 clustered 索引。
18.儘可能使用數字型字段。若僅僅含數值信息的字段儘可能不要設計爲字符型,這會減小查詢和鏈接的性能。並會添加存儲開銷。這是因爲引擎在處理查詢和連 接時會逐個比較字符串中每一個字符,而對於數字型而言僅僅需要比較一次就夠了。
19.儘量的使用 varchar/nvarchar 取代 char/nchar ,因爲首先變長字段存儲空間小,可以節省存儲空間,其次對於查詢來講,在一個相對較小的字段內搜索效率顯然要高些。
20.不論什麼地方都不要使用
select * from t
用詳細的字段列表取代「*」,不要返回用不到的不論什麼字段。
21.儘可能使用表變量來取代暫時表。假設表變量包括大量數據,請注意索引很是有限(僅僅有主鍵索引)。
22.避免頻繁建立和刪除暫時表。以減小系統表資源的消耗。
暫時表並不是不可以使用,適當地使用它們可以使某些例程更有效,好比。當需要反覆引用大型表或常用表中的某個數據集時。但是。對於一次性事件, 最好使用導出表。
23.在新建暫時表時,假設一次性插入數據量很是大,那麼可以使用 select into 取代 create table,避免形成大量 log ,以提快速度;假設數據量不大。爲了緩和系統表的資源,應先create table。而後insert。
24.假設使用到了暫時表,在存儲過程的最後務必將所有的暫時表顯式刪除,先 truncate table 。而後 drop table 。這樣可以避免系統表的較長時間鎖定。
25.儘可能避免使用遊標,因爲遊標的效率較差,假設遊標操做的數據超過1萬行,那麼就應該考慮改寫。
26.使用基於遊標的方法或暫時表方法以前,應先尋找基於集的解決方式來解決這個問題,基於集的方法一般更有效。
27.與暫時表同樣。遊標並不是不可以使用。對小型數據集使用 FAST_FORWARD 遊標一般要優於其它逐行處理方法。尤爲是在必須引用幾個表才幹得到所需的數據時。
在結果集中包括「合計」的例程一般要比使用遊標運行的速度快。假設開發時 間贊成,基於遊標的方法和基於集的方法都可以嘗試一下。看哪種方法的效果更好。
28.在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。無需在運行存儲過程和觸發器的每一個語句後向client發送 DONE_IN_PROC 消息。
29.儘可能避免大事務操做,提升系統併發能力。
30.儘可能避免向client返回大數據量,若數據量過大,應該考慮相應需求是否合理。
實際案例分析:拆分大的 DELETE 或INSERT 語句,批量提交SQL語句
假設你需要在一個在線的站點上去運行一個大的 DELETE 或 INSERT 查詢,你需要很是當心。要避免你的操做讓你的整個站點中止相應。因爲這兩個操做是會鎖表的。表一鎖住了,別的操做都進不來了。
Apache 會有很是多的子進程或線程。
因此,其工做起來至關有效率,而咱們的server也不但願有太多的子進程。線程和數據庫連接。這是極大的佔server資源的事情,尤爲是內存。
假設你把你的表鎖上一段時間,比方30秒鐘,那麼對於一個有很是高訪問量的站點來講,這30秒所積累的訪問進程/線程,數據庫連接,打開的文件數,可能不只僅會讓你的WEB服務崩潰,還可能會讓你的整臺server立刻掛了。
因此,假設你有一個大的處理。你必定把其拆分,使用 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) }
Facebook 開源動畫庫 Pop 的 GitHub 主頁:facebook/pop · GitHub,介紹:Playing with Pop (i)
Canvas 項目主頁:Canvas - Simplify iOS Development,介紹:Animate in Xcode Without Code
拿 Canvas 來和 Pop 比事實上不大合適,雖然二者都自稱「動畫庫」,但是「庫」這個詞的含義有所差異。本質上 Canvas 是一個「動畫合集」而 Pop 是一個「動畫引擎」。
先說 Canvas。
Canvas 的目的是「Animate in Xcode Without Code」。開發人員可以經過在 Storyboard 中指定 User Defined Runtime Attributes 來實現一些 Canvas 中預設的動畫。也就是他站點上能看到的那些。但是除了更修改畫的 delay 和 duration 基本上不能調整其它的參數。
Pop 就不同了。
假設說 Canvas 是對 Core Animation 的封裝。Pop 則是對 Core Animation(以及 UIDynamics)的再實現。
Pop 語法上和 Core Animation 類似。效果上則不像 Canvas 那麼生硬(時間四等分,振幅硬編碼)。這使得對 Core Animation 有了解的程序猿可以很是輕鬆地把原來的「靜態動畫」轉換成「動態動畫」。
同一時候 Pop 又往前多走了一步。既然動畫的本質是依據時間函數來作插值。那麼理論上不論什麼一個對象的不論什麼一個值都可以用來作插值。而不只僅是 Core Animation 裏定死的那一堆大小、位移、旋轉、縮放等 animatable properties。
REST是一種架構風格。其核心是面向資源,REST專門針對網絡應用設計和開發方式,以減小開發的複雜性,提升系統的可伸縮性。REST提出設計概念和準則爲:
1.網絡上的所有事物都可以被抽象爲資源(resource) 2.每一個資源都有惟一的資源標識(resource identifier)。對資源的操做不會改變這些標識 3.所有的操做都是無狀態的
REST簡化開發,其架構遵循CRUD原則,該原則告訴咱們對於資源(包括網絡資源)僅僅需要四種行爲:建立。獲取,更新和刪除就可以完畢相關的操做和處理。您可以經過統一資源標識符(Universal Resource Identifier。URI)來識別和定位資源,並且針對這些資源而運行的操做是經過 HTTP 規範定義的。其核心操做僅僅有GET,PUT,POST,DELETE。
因爲REST強制所有的操做都必須是stateless的,這就沒有上下文的約束。假設作分佈式。集羣都不需要考慮上下文和會話保持的問題。極大的提升系統的可伸縮性。
RESTful架構:
(1)每一個URI表明一種資源;
(2)client和server之間。傳遞這樣的資源的某種表現層;
(3)client經過四個HTTP動詞,對server端資源進行操做,實現"表現層狀態轉化"。
這個類庫提供一個UIImageView類別以支持載入來自網絡的遠程圖片。具備緩存管理、異步下載、同一個URL下載次數控制和優化等特徵。
SDWebImage 載入圖片的流程
1.入口 setImageWithURL:placeholderImage:options: 會先把 placeholderImage 顯示,而後 SDWebImageManager 依據 URL 開始處理圖片。
2.進入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交給 SDImageCache 從緩存查找圖片是否已經下載 queryDiskCacheForKey:delegate:userInfo:.
3.先從內存圖片緩存查找是否有圖片。假設內存中已經有圖片緩存,SDImageCacheDelegate 回調 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。
4.SDWebImageManagerDelegate 回調 webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等前端展現圖片。
5.假設內存緩存中沒有,生成 NSInvocationOperation 加入到隊列開始從硬盤查找圖片是否已經緩存。
6.依據 URLKey 在硬盤緩存文件夾下嘗試讀取圖片文件。這一步是在 NSOperation 進行的操做,因此回主線程進行結果回調 notifyDelegate:。
7.假設上一操做從硬盤讀取到了圖片,將圖片加入到內存緩存中(假設空暇內存太小,會先清空內存緩存)。SDImageCacheDelegate 回調 imageCache:didFindImage:forKey:userInfo:。進而回調展現圖片。
8.假設從硬盤緩存文件夾讀取不到圖片,說明所有緩存都不存在該圖片,需要下載圖片,回調 imageCache:didNotFindImageForKey:userInfo:。
9.共享或又一次生成一個下載器 SDWebImageDownloader 開始下載圖片。
10.圖片下載由 NSURLConnection 來作,實現相關 delegate 來推斷圖片下載中、下載完畢和下載失敗。
11.connection:didReceiveData: 中利用 ImageIO 作了按圖片下載進度載入效果。
12.connectionDidFinishLoading: 數據下載完畢後交給 SDWebImageDecoder 作圖片解碼處理。
13.圖片解碼處理在一個 NSOperationQueue 完畢。不會拖慢主線程 UI。假設有需要對下載的圖片進行二次處理。最好也在這裏完畢,效率會好很是多。
14.在主線程 notifyDelegateOnMainThreadWithInfo: 宣告解碼完畢。imageDecoder:didFinishDecodingImage:userInfo: 回調給 SDWebImageDownloader。
15.imageDownloader:didFinishWithImage: 回調給 SDWebImageManager 告知圖片下載完畢。
16.通知所有的 downloadDelegates 下載完畢,回調給需要的地方展現圖片。
17.將圖片保存到 SDImageCache 中,內存緩存和硬盤緩存同一時候保存。寫文件到硬盤也在以單獨 NSInvocationOperation 完畢,避免拖慢主線程。
18.SDImageCache 在初始化的時候會註冊一些消息通知,在內存警告或退到後臺的時候清理內存圖片緩存。應用結束的時候清理過時圖片。
19.SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。
20.SDWebImagePrefetcher 可以預先下載圖片,方便興許使用。
SDWebImage庫的做用
經過對UIImageView的類別擴展來實現異步載入替換圖片的工做。
主要用到的對象:
一、UIImageView (WebCache)類別。入口封裝。實現讀取圖片完畢後的回調
二、SDWebImageManager,對圖片進行管理的中轉站,記錄那些圖片正在讀取。
向下層讀取Cache(調用SDImageCache),或者向網絡讀取對象(調用SDWebImageDownloader) 。
實現SDImageCache和SDWebImageDownloader的回調。
三、SDImageCache。依據URL的MD5摘要對圖片進行存儲和讀取(實現存在內存中或者存在硬盤上兩種實現)
實現圖片和內存清理工做。
四、SDWebImageDownloader,依據URL向網絡讀取數據(實現部分讀取和所有讀取後再通知回調兩種方式)
其它類:
SDWebImageDecoder。異步對圖像進行了一次解壓⋯⋯
一、SDImageCache是怎麼作數據管理的?
SDImageCache分兩個部分。一個是內存層面的,一個是硬盤層面的。內存層面的至關是個緩存器,以Key-Value的形式存儲圖片。當內存不夠的時候會清除所有緩存圖片。用搜索文件系統的方式作管理,文件替換方式是以時間爲單位,剔除時間大於一週的圖片文件。當SDWebImageManager向SDImageCache要資源時,先搜索內存層面的數據,假設有直接返回,沒有的話去訪問磁盤。將圖片從磁盤讀取出來。而後作Decoder,將圖片對象放到內存層面作備份,再返回調用層。
二、爲啥必須作Decoder?
因爲UIImage的imageWithData函數是每次繪圖的時候纔將Data解壓成ARGB的圖像,因此在每次繪圖的時候,會有一個解壓操做,這樣效率很是低,但是僅僅有瞬時的內存需求。爲了提升效率經過SDWebImageDecoder將包裝在Data下的資源解壓。而後畫在另一張圖片上。這樣這張新圖片就再也不需要反覆解壓了。
這樣的作法是典型的空間換時間的作法。