當iOS設備上的App應用閃退時,操做系統會生成一個crash日誌,保存在設備上。crash日誌上有不少有用的信息,好比每一個正在執行線程的完整堆棧跟蹤信息和內存映像,這樣就可以經過解析這些信息進而定位crash發生時的代碼邏輯,從而找到App閃退的緣由。一般來講,crash產生來源於兩種問題:違反iOS系統規則致使的crash和App代碼邏輯BUG致使的crash,下面分別對他們進行分析。html
違反iOS系統規則產生crash的三種類型前端
(1) 內存報警閃退mysql
當iOS檢測到內存太低時,它的VM系統會發出低內存警告通知,嘗試回收一些內存;若是狀況沒有獲得足夠的改善,iOS會終止後臺應用以回收更多內存;最後,若是內存仍是不足,那麼正在運行的應用可能會被終止掉。在Debug模式下,能夠主動將客戶端執行的動做邏輯寫入一個log文件中,這樣程序童鞋能夠將內存預警的邏輯寫入該log文件,當發生以下截圖中的內存報警時,就是提醒當前客戶端性能內存吃緊,能夠經過Instruments工具中的Allocations 和 Leaks模塊庫來發現內存分配問題和內存泄漏問題。git
(2) 響應超時程序員
當應用程序對一些特定的事件(好比啓動、掛起、恢復、結束)響應不及時,蘋果的Watchdog機制會把應用程序幹掉,並生成一份相應的crash日誌。這些事件與下列UIApplicationDelegate方法相對應,當遇到Watchdog日誌時,能夠檢查上圖中的幾個方法是否有比較重的阻塞UI的動做。github
application:didFinishLaunchingWithOptions:
applicationWillResignActive:
applicationDidEnterBackground:
applicationWillEnterForeground:
applicationDidBecomeActive:
applicationWillTerminate:
(3) 用戶強制退出web
一看到「用戶強制退出」,首先可能想到的雙擊Home鍵,而後關閉應用程序。不過這種場景通常是不會產生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的四個做用:
主線程的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中的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 種基本狀態,它們是:初始態,執行態,等待狀態,就緒狀態,終止狀態。
線程,在網絡或多用戶環境下,一個服務器一般須要接收大量且不肯定數量用戶的併發請求,爲每個請求都建立一個進程顯然是行不通的,——不管是從系統資源開銷方面或是響應用戶請求的效率方面來看。所以,操做系統中線程的概念便被引進了。線程,是進程的一部分,一個沒有線程的進程能夠被看做是單線程的。線程有時又被稱爲輕權進程或輕量級進程,也是 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 。無需在執行存儲過程和觸發器的每一個語句後向客戶端發送 DONE_IN_PROC 消息。
29.儘可能避免大事務操做,提升系統併發能力。
30.儘可能避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。
實際案例分析:拆分大的 DELETE 或INSERT 語句,批量提交SQL語句
若是你須要在一個在線的網站上去執行一個大的 DELETE 或 INSERT 查詢,你須要很是當心,要避免你的操做讓你的整個網站中止相應。由於這兩個操做是會鎖表的,表一鎖住了,別的操做都進不來了。
Apache 會有不少的子進程或線程。因此,其工做起來至關有效率,而咱們的服務器也不但願有太多的子進程,線程和數據庫連接,這是極大的佔服務器資源的事情,尤爲是內存。
若是你把你的表鎖上一段時間,好比30秒鐘,那麼對於一個有很高訪問量的站點來講,這30秒所積累的訪問進程/線程,數據庫連接,打開的文件數,可能不只僅會讓你的WEB服務崩潰,還可能會讓你的整臺服務器立刻掛了。
因此,若是你有一個大的處理,你必定把其拆分,使用 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)客戶端和服務器之間,傳遞這種資源的某種表現層;
(3)客戶端經過四個HTTP動詞,對服務器端資源進行操做,實現」表現層狀態轉化」。
這個類庫提供一個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下的資源解壓,而後畫在另一張圖片上,這樣這張新圖片就再也不須要重複解壓了。
這種作法是典型的空間換時間的作法。