1. 程式人生 > >iOS知識點匯總

iOS知識點匯總

建立索引 led 對數 xcod 基礎設施 tomat fault 創建 事件處理

1.怎樣追蹤app崩潰率。怎樣解決線上閃退

當iOS設備上的App應用閃退時。操作系統會生成一個crash日誌。保存在設備上。crash日誌上有非常多實用的信息,比方每個正在運行線程的完整堆棧跟蹤信息和內存映像,這樣就能夠通過解析這些信息進而定位crash發生時的代碼邏輯,從而找到App閃退的原因。

通常來說,crash產生來源於兩種問題:違反iOS系統規則導致的crash和App代碼邏輯BUG導致的crash,以下分別對他們進行分析。

違反iOS系統規則產生crash的三種類型

(1) 內存報警閃退

當iOS檢測到內存過低時,它的VM系統會發出低內存警告通知,嘗試回收一些內存;假設情況沒有得到足夠的改善,iOS會終止後臺應用以回收很多其它內存;最後。假設內存還是不足,那麽正在運行的應用可能會被終止掉。在Debug模式下,能夠主動將client運行的動作邏輯寫入一個log文件裏,這樣程序童鞋能夠將內存預警的邏輯寫入該log文件,當發生例如以下截圖中的內存報警時,就是提醒當前client性能內存吃緊,能夠通過Instruments工具中的Allocations 和 Leaks模塊庫來發現內存分配問題和內存泄漏問題。

(2) 響應超時
當應用程序對一些特定的事件(比方啟動、掛起、恢復、結束)響應不及時。蘋果的Watchdog機制會把應用程序幹掉。並生成一份相應的crash日誌。

這些事件與下列UIApplicationDelegate方法相相應。當遇到Watchdog日誌時,能夠檢查上圖中的幾個方法是否有比較重的堵塞UI的動作。 

application:didFinishLaunchingWithOptions: 
applicationWillResignActive:
applicationDidEnterBackground: 
applicationWillEnterForeground:
applicationDidBecomeActive:
applicationWillTerminate:

(3) 用戶強制退出
一看到“用戶強制退出”,首先可能想到的雙擊Home鍵,然後關閉應用程序。

只是這樣的場景通常是不會產生crash日誌的。由於雙擊Home鍵後,全部的應用程序都處於後臺狀態,而iOS隨時都有可能關閉後臺進程,當應用堵塞界面並停止響應時這樣的場景才會產生crash日誌。這裏指的“用戶強制退出”場景。是略微比較復雜點的操作:先按住電源鍵,直到出現“滑動關機”的界面時。再按住Home鍵,這時候當前應用程序會被終止掉,而且產生一份相應事件的crash日誌。

應用邏輯的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信息

由於自己的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)。

解決線上閃退
首先保證。公布前充分測試。

公布後依舊有閃退現象,查看崩潰日誌,及時修復並公布。

2.什麽是事件響應鏈。點擊屏幕時是怎樣互動的。事件的傳遞。

技術分享圖片
事件響應鏈

對於IOS設備用戶來說,他們操作設備的方式主要有三種:觸摸屏幕、晃動設備、通過遙控設施控制設備。相應的事件類型有以下三種:

1、觸屏事件(Touch Event)

2、運動事件(Motion Event)

3、遠端控制事件(Remote-Control Event)

響應者鏈(Responder Chain)
響應者對象(Responder Object),指的是有響應和處理事件能力的對象。響應者鏈就是由一系列的響應者對象構成的一個層次結構。

UIResponder是全部響應對象的基類,在UIResponder類中定義了處理上述各種事件的接口。

我們熟悉的UIApplication、 UIViewController、UIWindow和全部繼承自UIView的UIKit類都直接或間接的繼承自UIResponder。所以它們的實例都是能夠構成響應者鏈的響應者對象。

響應者鏈有以下特點:
1、響應者鏈通常是由視圖(UIView)構成的;
2、一個視圖的下一個響應者是它視圖控制器(UIViewController)(假設有的話),然後再轉給它的父視圖(Super View)。
3、視圖控制器(假設有的話)的下一個響應者為其管理的視圖的父視圖;
4、單例的窗體(UIWindow)的內容視圖將指向窗體本身作為它的下一個響應者
須要指出的是,Cocoa Touch應用不像Cocoa應用,它僅僅有一個UIWindow對象,因此整個響應者鏈要簡單一點。
5、單例的應用(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

3.Run Loop是什麽。使用的目的。何時使用和關註點

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:

  1. kCFRunLoopDefaultMode: App的默認 Mode,通常主線程是在這個 Mode 下運行的。
  2. UITrackingRunLoopMode: 界面跟蹤 Mode,用於 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其它 Mode 影響。
  3. UIInitializationRunLoopMode: 在剛啟動 App 時第進入的第一個 Mode,啟動完畢後就不再使用。
  4. GSEventReceiveRunLoopMode: 接受系統事件的內部 Mode。通經常使用不到。
  5. kCFRunLoopCommonModes: 這是一個占位的 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能夠使你的線程在有工作的時候工作。沒有工作的時候休眠,這能夠大大節省系統資源。

技術分享圖片
RunLoop

什麽時候使用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…的方法
使線程周期性工作

關註點

  1. Cocoa中的NSRunLoop類並非線程安全的
    我們不能再一個線程中去操作另外一個線程的run loop對象,那非常可能會造成意想不到的後果。只是幸運的是CoreFundation中的不透明類CFRunLoopRef是線程安全的,而且兩種類型的run loop全然能夠混合使用。Cocoa中的NSRunLoop類能夠通過實例方法:

     - (CFRunLoopRef)getCFRunLoop;

    獲取相應的CFRunLoopRef類,來達到線程安全的目的。

  2. 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);
  3. Run loop同一時候也負責autorelease pool的創建和釋放
    在使用手動的內存管理方式的項目中,會經經常使用到非常多自己主動釋放的對象,假設這些對象不能夠被即時釋放掉,會造成內存占用量急劇增大。

    Run loop就為我們做了這樣的工作,每當一個運行循環結束的時候,它都會釋放一次autorelease pool,同一時候pool中的全部自己主動釋放類型變量都會被釋放掉。

4. ARC和MRC

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下意義同樣。

5. 線程和進程

進程。是並發運行的程序在運行過程中分配和管理資源的基本單位,是一個動態概念,竟爭計算機系統資源的基本單位。每個進程都有一個自己的地址空間,即進程空間或(虛空間)。進程空間的大小 僅僅與處理機的位數有關,一個 16 位好處理機的進程空間大小為 216 ,而 32 位處理機的進程空間大小為 232 。進程至少有 5 種基本狀態。它們是:初始態,運行態。等待狀態,就緒狀態,終止狀態。

線程,在網絡或多用戶環境下,一個server通常須要接收大量且不確定數量用戶的並發請求,為每個請求都創建一個進程顯然是行不通的。——不管是從系統資源開銷方面或是響應用戶請求的效率方面來看。因此,操作系統中線程的概念便被引進了。

線程,是進程的一部分。一個沒有線程的進程能夠被看作是單線程的。

線程有時又被稱為輕權進程或輕量級進程,也是 CPU 調度的一個基本單位。

進程的運行過程是線狀的,雖然中間會發生中斷或暫停,但該進程所擁有的資源僅僅為該線狀運行過程服務。一旦發生進程上下文切換,這些資源都是要被保護起來的。

這是進程宏觀上的運行過程。

而進程又可有單線程進程與多線程進程兩種。我們知道,進程有 一個進程控制塊 PCB 。相關程序段 和 該程序段對其進行操作的數據結構集 這三部分,單線程進程的運行過程在宏觀上是線性的。微觀上也僅僅有單一的運行過程。而多線程進程在宏觀上的運行過程同樣為線性的,但微觀上卻能夠有多個運行操作(線程),如不同代碼片段以及相關的數據結構集。線程的改變僅僅代表了 CPU 運行過程的改變。而沒有發生進程所擁有的資源變化。除了 CPU 之外,計算機內的軟硬件資源的分配與線程無關,線程僅僅能共享它所屬進程的資源。與進程控制表和 PCB 相似。每個線程也有自己的線程控制表 TCB ,而這個 TCB 中所保存的線程狀態信息則要比 PCB 表少得多,這些信息主要是相關指針用堆棧(系統棧和用戶棧),寄存器中的狀態數據。進程擁有一個完整的虛擬地址空間,不依賴於線程而獨立存在;反之,線程是進程的一部分。沒有自己的地址空間。與進程內的其它線程一起共享分配給該進程的全部資源

線程能夠有效地提高系統的運行效率,但並非在全部計算機系統中都是適用的。如某些非常少做進程調度和切換的實時系統。使用線程的好處是有多個任務須要處理機處理時,減少處理機的切換時間;而且,線程的創建和結束所須要的系統開銷也比進程的創建和結束要小得多。最適用使用線程的系統是多處理機系統和網絡系統或分布式系統。

6. 尋經常常使用的多線程處理方式及優缺點

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 就永遠等待著。

7. 大量數據表的優化方案

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 語句,假設僅僅更改1、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)
}

8. 經常使用到的動畫庫

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。

9. Restful架構

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端資源進行操作,實現"表現層狀態轉化"。

10. 請分析下SDWebImage的原理

這個類庫提供一個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的類別擴展來實現異步載入替換圖片的工作。

主要用到的對象:
1、UIImageView (WebCache)類別。入口封裝。實現讀取圖片完畢後的回調
2、SDWebImageManager,對圖片進行管理的中轉站,記錄那些圖片正在讀取。


向下層讀取Cache(調用SDImageCache),或者向網絡讀取對象(調用SDWebImageDownloader) 。
實現SDImageCache和SDWebImageDownloader的回調。
3、SDImageCache。依據URL的MD5摘要對圖片進行存儲和讀取(實現存在內存中或者存在硬盤上兩種實現)
實現圖片和內存清理工作。


4、SDWebImageDownloader,依據URL向網絡讀取數據(實現部分讀取和全部讀取後再通知回調兩種方式)

其它類:
SDWebImageDecoder。異步對圖像進行了一次解壓??

1、SDImageCache是怎麽做數據管理的?

SDImageCache分兩個部分。一個是內存層面的,一個是硬盤層面的。內存層面的相當是個緩存器,以Key-Value的形式存儲圖片。當內存不夠的時候會清除全部緩存圖片。用搜索文件系統的方式做管理,文件替換方式是以時間為單位,剔除時間大於一周的圖片文件。當SDWebImageManager向SDImageCache要資源時,先搜索內存層面的數據,假設有直接返回,沒有的話去訪問磁盤。將圖片從磁盤讀取出來。然後做Decoder,將圖片對象放到內存層面做備份,再返回調用層。

2、為啥必須做Decoder?


由於UIImage的imageWithData函數是每次繪圖的時候才將Data解壓成ARGB的圖像,所以在每次繪圖的時候,會有一個解壓操作,這樣效率非常低,可是僅僅有瞬時的內存需求。為了提高效率通過SDWebImageDecoder將包裝在Data下的資源解壓。然後畫在另外一張圖片上。這樣這張新圖片就不再須要反復解壓了。


這樣的做法是典型的空間換時間的做法。



文/吳白(簡書作者)
原文鏈接:http://www.jianshu.com/p/8f16613861fa
著作權歸作者全部。轉載請聯系作者獲得授權,並標註“簡書作者”。

iOS知識點匯總