iOS面試總結(jié)
1. 網(wǎng)絡
-
HTTP協(xié)議(HyperText Transfer Protocol)的請求和響應
- 請求: 請求頭, 請求行, 請求體
- 請求行: 指定請求方法, 請求路徑 協(xié)議版本等信息
- 請求頭: 描述客戶端環(huán)境, 例如: host要請求的主機地址, UserAgent客戶端類型, Accept 可接受數(shù)據(jù)類型, Accept_language可接受語言
- 請求體: 客戶端要發(fā)送的具體數(shù)據(jù), 例如上傳時的上傳數(shù)據(jù)
- 響應: 狀態(tài)行,響應頭, 響應數(shù)據(jù)
- 狀態(tài)行: 包含http協(xié)議版本, 狀態(tài)碼
- 響應頭: 服務器描述, 數(shù)據(jù)類型描述例如 Sever, Content-Type
- 響應數(shù)據(jù): 請求所獲得的后臺數(shù)據(jù)
- 請求: 請求頭, 請求行, 請求體
-
HTTP的特點
無狀態(tài): 服務器不會保留客戶端狀態(tài), 不會記憶上次的狀態(tài), 不受前面請求的影響, 客戶端每次請求獨立, 每次請求需帶上自己的狀態(tài)
-
持久鏈接: 每個鏈接可以處理多個事務, 特殊強調(diào)http1.0及其之前版本都是非持久鏈接(每個鏈接只能處理一個事務)
持久鏈接如何結(jié)束?
- 如果有Content-length字段, 可以判斷接受數(shù)據(jù)是否完成,
- 如果是分塊傳輸, 響應頭中不包含Content-length, 服務器傳輸完成萬發(fā)送一個空數(shù)據(jù)塊, 當客戶端收到空數(shù)據(jù)塊時算作數(shù)據(jù)接受完成
-
HTTP的請求方式
GET仑撞、POST谒出、PUT、DELETE成榜、HEAD胰坟、OPTIONS等
-
GET和和POST的區(qū)別:
get請求參數(shù)拼接在url后面, post的參數(shù)拼接在請求體里面,相對安全(在http抓包情況下依然不安全)
get參數(shù)長度有限制2048, post無限制
get獲取資源是安全的(不會修改服務器資源), 冪等的(多次執(zhí)行結(jié)果相同), 可緩存的(可以直接有CDN緩存, 減輕服務器負擔), post相反
-
HTTP常用狀態(tài)碼含義 參考文檔點擊 HTTP狀態(tài)碼總結(jié)
-
1xx 表示消息
- 100 初始的請求已經(jīng)接受奶镶,客戶應當繼續(xù)發(fā)送請求的其余部分, 例如post請求(兩段)的第一段請求收到的就是100
- 101 服務器將遵從客戶的請求轉(zhuǎn)換到另外一種協(xié)議
-
2xx 表示成功
- 200: OK, 完全正常
3xx 表示重定向
-
4xx 表示請求錯誤
- 400 請求出現(xiàn)語法錯誤
- 401 訪問收到密碼保護的頁面
- 403 服務器拒絕執(zhí)行,
- 404 服務器資源未找到
- 405 請求方法(get, post等)對指定資源不適用,
-
5xx 表示服務器錯誤
- 500 服務器遇到意料不到的錯誤, 不能完成客戶端請求
-
-
TCP和UDP的特點, 參考鏈接 簡單理解TCP/IP協(xié)議
-
TCP協(xié)議(Transport Control Protocol诉位,傳輸控制協(xié)議): 是一種面向連接胁孙、可靠的唠倦、基于字節(jié)流的傳輸層協(xié)議,采用了確認機制涮较、超時重傳機制稠鼻,還會對接收到的TCP報文段進行重新排列整理。(TCP報頭含20字節(jié)定長狂票、選項和填充<選項和填充小于等于40字節(jié)>)(TCP是一種面向連接的傳輸層協(xié)議候齿。它可以保證兩端通信主機之間的通信可達。TCP能夠正確處理傳輸過程中丟包闺属、傳輸順序亂掉等異常情況慌盯。)
TCP能保證可靠性、穩(wěn)定性, 適用于可靠性較高的服務
-
UDP協(xié)議:(User Datagram Protocol掂器,用戶數(shù)據(jù)報協(xié)議)是一種不可靠無連接的傳輸層協(xié)議亚皂,不考慮流控制、錯誤控制国瓮,沒有重傳機制灭必,不會對分組進行順序檢查和排序狞谱。
UDP控制選項少,無須建立連接禁漓,從而使得數(shù)據(jù)傳輸過程中的延遲小跟衅、數(shù)據(jù)傳輸效率高, 適用于實時性要求高的程序
-
-
網(wǎng)絡七層模型
應用層, 表示層, 會話層, 傳輸層, 網(wǎng)絡層, 數(shù)據(jù)鏈路層, 物理層
- 應用層: 網(wǎng)絡應用如: http ftp, pop, smtp等應用協(xié)議
- 表示層: 定義數(shù)據(jù)格式及加密, 例如,F(xiàn)TP允許你選擇以二進制或ASCII格式傳輸璃饱。如果選擇二進制与斤,那么發(fā)送方和接收方不改變文件的內(nèi)容。如果選擇ASCII格式荚恶,發(fā)送方將把文本從發(fā)送方的字符集轉(zhuǎn)換成標準的ASCII后發(fā)送數(shù)據(jù)撩穿。在接收方將標準的ASCII轉(zhuǎn)換成接收方計算機的字符集。示例:加密谒撼,ASCII等食寡。對應網(wǎng)絡協(xié)議: Telnet, Rlogin, SNMP, Copher等
- 會話層: 它定義了如何開始、控制和結(jié)束一個會話廓潜,包括對多個雙向消息的控制和管理抵皱,以便在只完成連續(xù)消息的一部分時可以通知應用,從而使表示層看到的數(shù)據(jù)是連續(xù)的辩蛋,在某些情況下呻畸,如果表示層收到了所有的數(shù)據(jù),則用數(shù)據(jù)代表表示層悼院。示例:RPC伤为,SQL等
- 傳輸層: 這層的功能包括是否選擇差錯恢復協(xié)議還是無差錯恢復協(xié)議,及在同一主機上對不同應用的數(shù)據(jù)流的輸入進行復用据途,還包括對收到的順序不對的數(shù)據(jù)包的重新排序功能绞愚。示例:TCP,UDP颖医,SPX位衩。
- 網(wǎng)絡層: 這層對端到端的包傳輸進行定義,它定義了能夠標識所有結(jié)點的邏輯地址熔萧,還定義了路由實現(xiàn)的方式和學習的方式糖驴。為了適應最大傳輸單元長度小于包長度的傳輸介質(zhì),網(wǎng)絡層還定義了如何將一個包分解成更小的包的分段方法佛致。示例:IP遂赠,IPX, UUCP等。
- 數(shù)據(jù)鏈路層: 它定義了在單個鏈路上如何傳輸數(shù)據(jù)晌杰。這些協(xié)議與被討論的各種介質(zhì)有關(guān)。示例:ATM筷弦,FDDI等肋演。
- 物理層: OSI的物理層規(guī)范是有關(guān)傳輸介質(zhì)的特性抑诸,這些規(guī)范通常也參考了其他組織制定的標準。連接頭爹殊、幀蜕乡、幀的使用、電流梗夸、編碼及光調(diào)制等都屬于各種物理層規(guī)范中的內(nèi)容层玲。物理層常用多個規(guī)范完成對所有細節(jié)的定義。示例:Rj45反症,IEEE 802.3 等辛块。
-
Socket
客戶端: socket()->connect()->write()/read()->close()
服務端: socket()->bind()->listen()->accept()->read()/write()->close()
-
數(shù)據(jù)解析:
- JSON: 使用NSJSONSerialization解析
-
XML: SAX解析, DOM解析
- SAX: NSXMLParser 實現(xiàn)代理方法解析, 特點從上往下依次解析
- DOM: 特點一次性讀取解析
-
網(wǎng)絡安全
Base64加密, 可逆加密
MD5/SHA1/SHA256等 不可逆加密
-
對稱加密和非對稱加密
對稱加密: 加解密使用同一個秘鑰, 代表: DES, AES等, 有點效率高, 缺點, 秘鑰交換時安全不能保障
非對稱加密: 加解密雙方使用不同的秘鑰, 加密使用公鑰加密, 解密是用私鑰解密, 公鑰是公開的 代表: RSA, ECC, DSA(數(shù)字簽名用)
-
HTTPS
-
HTTPS中的S含義
S代表的是SSL/TLS協(xié)議, 即: Secure Sockets layer(安全套接層)和Transport Layer Security(安全傳輸層)
-
HTTPS的建立流程
HTTPS為了兼顧安全與效率,同時采用了對稱加密算法和非對稱加密算法, 通信過程主要會設計三個秘鑰: 服務器端的公鑰和私鑰, 用來進行非對稱加密, 客戶端隨機生成的隨機秘鑰, 用來進行對稱加密
- 客戶端訪問HTTPS鏈接, 告訴服務器客戶端支持的加密算法列表, 和隨機數(shù)C
- 服務器選擇一種對稱算法(如AES), 一種非對稱算法, 一種MAC算法發(fā)送給客戶端, 同時把數(shù)字證書和隨機數(shù)S發(fā)給客戶端
- 客戶端驗證服務器的數(shù)字證書, 客戶端生成前主秘鑰, 并使用服務器的公鑰加密發(fā)送個服務器, 并使用前主秘鑰/隨機數(shù)C/隨機數(shù)S, 生成會話秘鑰
- 服務器解密得到前主秘鑰, 通過前主秘鑰/隨機數(shù)C/隨機數(shù)S生成會話秘鑰
- 數(shù)據(jù)加密傳輸
-
-
NSURLSession
-
NSURLSession的優(yōu)勢
- 支持后臺上傳下載
- 可以暫停, 停止, 重啟網(wǎng)絡任務
- 可以對緩存策略,session類型铅碍、任務類型(比如上傳润绵、下載等任務)進行單獨的配置
- 支持block回調(diào)使用方便
- 支持IPV6網(wǎng)絡
-
NSURLsession的使用
NSURLsession 是一個管理類, 可以通過NSURLSessionConfiguration進行配置
URLSessionTask是任務的父類, 包含兩個子類URLSessionDataTask和URLSessionDownloadTask, 其中URLSessionDataTask有一個子類URLSessionUploadTask, URLSessionDataTask用來處理一般網(wǎng)絡請求
URLSessionDownloadTask處理下載任務, URLSessionUploadTask上傳任務
-
2. 多線程
-
iOS開發(fā)常用的多線程方式
- pthread: C語言實現(xiàn), 可以跨平臺, 線程生命周期需要手動管理
- NSThread: OC實現(xiàn), 線程生命周期需要手動管理
- GCD: 蘋果對多核性能優(yōu)化, C語言, 線程生命周期自動管理
- NSOperation: 對GCD封裝, 線程生命周期自動管理
?
-
NSThread簡單介紹
創(chuàng)建線程對象: 顯示創(chuàng)建(alloc init)和隱式創(chuàng)建(performSelector.....)
線程狀態(tài):新建, 就緒(start), 阻塞(sleep), 運行, 死亡(exit)
常用屬性: name(當前線程名字), threadPriority(線程優(yōu)先級), isMainThread等
-
GCD和NSOperation對比
- GCD執(zhí)行效率更高,而且由于隊列中執(zhí)行的是由block構(gòu)成的任務胞谈,這是一個輕量級的數(shù)據(jù)結(jié)構(gòu)尘盼,寫起來更方便
- GCD只支持FIFO的隊列,而NSOperationQueue可以通過設置最大并發(fā)數(shù)烦绳,設置優(yōu)先級卿捎,添加依賴關(guān)系等調(diào)整執(zhí)行順序
- NSOperationQueue甚至可以跨隊列設置依賴關(guān)系,但是GCD只能通過設置串行隊列径密,或者在隊列內(nèi)添加barrier(dispatch_barrier_async)任務午阵,才能控制執(zhí)行順序,較為復雜
- NSOperationQueue因為面向?qū)ο螅灾С諯VO睹晒,可以監(jiān)測operation是否正在執(zhí)行(isExecuted)趟庄、是否結(jié)束(isFinished)、是否取消(isCanceled)
-
什么情況下會出現(xiàn)死鎖
-
在主線程中將同步任務添加到主隊列會導致死鎖
- (void)viewDidLoad { [super viewDidLoad]; dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"deallock"); }); // Do any additional setup after loading the view, typically from a nib. }
-
在一個串行隊列任務中將 同步任務添加到當前的隊列中
dispatch_queue_t serialQueue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL); dispatch_async(serialQueue, ^{ dispatch_sync(serialQueue, ^{ NSLog(@"deadlock"); }); });
-
-
GCD中的線程柵欄
dispatch_barrier_async/dispatch_barrier_sync 分別表示同步柵欄和異步柵欄
柵欄的作用是可以將任務分塊執(zhí)行, 條件是任務必須在同一個隊列上, 否則無效
dispatch_queue_t concurrentQueue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT); for (NSInteger i = 0; i < 10; i++) { dispatch_async(concurrentQueue, ^{ NSLog(@"%zd",i); }); } dispatch_barrier_async(concurrentQueue, ^{ NSLog(@"barrier"); }); NSLog(@"哈哈哈"); for (NSInteger i = 10; i < 20; i++) { dispatch_async(concurrentQueue, ^{ NSLog(@"%zd",i); }); } 執(zhí)行結(jié)果: 哈哈哈 0-9 亂序打印 barrier 10-19亂序打印
由于采用的是異步柵欄, 所以柵欄后的任務會"哈哈哈"會提起執(zhí)行, 如果將柵欄換成同步柵欄則 "哈哈哈"一定是在"barrier"之后執(zhí)行
通過線程柵欄可以實現(xiàn)多度單寫, 即允許多個地方同時讀取數(shù)據(jù), 但是在寫入數(shù)據(jù)時只允許一個地方寫入
- (id)readDataForKey:(NSString *)key {
__block id result;
dispatch_sync(_concurrentQueue, ^{
result = [self valueForKey:key];
});
return result;
}
- (void)writeData:(id)data forKey:(NSString *)key {
dispatch_barrier_async(_concurrentQueue, ^{
[self setValue:data forKey:key];
});
}
-
GCD線程組的使用
需求多個網(wǎng)絡請求完成之后一并刷新UI
- (void)testGCDGroup { __block NSInteger number = 0; dispatch_group_t group = dispatch_group_create(); //A耗時操作 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(3); number += 2222; NSLog(@"A:%zd", number); }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self sendRequestWithCompletion:^(id response) { number += [response integerValue]; NSLog(@"B:%zd", number); }]; }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self sendRequestWithCompletion:^(id response) { number += [response integerValue]; NSLog(@"C:%zd", number); }]; }); // //B網(wǎng)絡請求 // dispatch_group_enter(group); // [self sendRequestWithCompletion:^(id response) { // number += [response integerValue]; // NSLog(@"B:%zd", number); // dispatch_group_leave(group); // // }]; // // //C網(wǎng)絡請求 // dispatch_group_enter(group); // [self sendRequestWithCompletion:^(id response) { // number += [response integerValue]; // NSLog(@"C:%zd", number); // dispatch_group_leave(group); // }]; // dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"finish: %zd", number); }); } - (void)sendRequestWithCompletion:(void (^)(id response))completion { //模擬一個網(wǎng)絡請求 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ sleep(2); dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(@1111); }); }); } 調(diào)用結(jié)果: B:1111 C:2222 A:4444 finish: 4444
使用方式如代碼所示, 先創(chuàng)建線程組, 然后使用dispatch_group_async, 將任務綁定到線程組, 最后使用dispatch_group_notify接受線程組任務完成的回調(diào). 其中也可以使用dispatch_group_enter(group)這種方式將任務添加到線程組, 不過任務結(jié)束之后要配合使用 dispatch_group_leave(group);
-
Dispatch Semaphore 信號量
Dispatch Semaphore 提供了三個函數(shù)
- dispatch_semaphore_create: 創(chuàng)建一個Semaphore并初始化總量
- dispatch_semaphore_signal: 發(fā)送信號, 讓信號總量加1
- dispatch_semaphore_wait: 可以是信號總量減1, 信號總量為0是進入等待
Dispatch Semaphore 在實際開發(fā)中主要用于:
-
保持線程同步伪很,將異步執(zhí)行任務轉(zhuǎn)換為同步執(zhí)行任務
- (void)testSemaphore { dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block NSInteger number = 0; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ number = 100; NSLog(@"執(zhí)行異步任務"); dispatch_semaphore_signal(semaphore); }); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"semaphore---end,number = %zd",number); } //打印結(jié)果如下: //執(zhí)行異步任務 //semaphore---end,number = 100
創(chuàng)建信號量初始化總量為0, 由于是異步執(zhí)行,所以直接執(zhí)行到dispatch_semaphore_wait, 如果信號量為0則一直等待阻斷線程, 當異步任務執(zhí)行完之后將信號量加1, 此時信號量等待函數(shù)終結(jié), 繼續(xù)執(zhí)行后面的任務
-
保證線程安全戚啥,為線程加鎖
- (void)testSemaphore2 { _semaphore = dispatch_semaphore_create(1); for (NSInteger i = 0; i < 100; i++) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self asyncTask]; }); } } - (void)asyncTask { dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER); _count++; sleep(1); NSLog(@"執(zhí)行任務:%zd",_count); dispatch_semaphore_signal(_semaphore); } ///執(zhí)行結(jié)果發(fā)現(xiàn), 是順序從1-100執(zhí)行
原因: 在子線程執(zhí)行并發(fā)任務時, 由于第一次執(zhí)行任務將信號量減1, 信號總量變?yōu)?, 當?shù)诙€任務進來時需要由于信號總量為0所以進入等待狀態(tài), 任務一執(zhí)行完之后將信號量加1, 任務二開始執(zhí)行, 并立即將信號總量減1變?yōu)?, 任務三繼續(xù)等待, 依次類推, 實現(xiàn)了線程加鎖目的
-
使用dispatch_once實現(xiàn)單例
//手寫單例 - (id)sharedInstance { static id instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; }) return instance; }
-
NSOperationQueue/NSOperation簡介
-
優(yōu)勢
可以添加任務依賴,方便控制執(zhí)行順序
可以設定操作執(zhí)行的優(yōu)先級
可以設置最大并發(fā)量
-
任務執(zhí)行狀態(tài)控制:isReady,isExecuting,isFinished,isCancelled
如果只是重寫NSOperation的main方法锉试,由底層控制變更任務執(zhí)行及完成狀態(tài)猫十,以及任務退出
如果重寫了NSOperation的start方法,自行控制任務狀態(tài)
系統(tǒng)通過KVO的方式移除isFinished==YES的NSOperation
-
基本操作
NSOperation是抽象類, 主要使用兩個子類: NSBlockOperation和NSInvocationOperation
可以直接添加Block任務NSOperation到隊列中執(zhí)行, 默認在當前線程執(zhí)行, NSBlockOperation添加多任務時會自動開啟新線程執(zhí)行
也可以將NSOperation添加到NSOperationQueue中執(zhí)行操作, 當maxConcurrentOperationCount = 1時為串行隊列, 大于1時為并發(fā)隊列
-
示例代碼
/** * 設置 MaxConcurrentOperationCount(最大并發(fā)操作數(shù)) */ - (void)setMaxConcurrentOperationCount { // 1.創(chuàng)建隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.設置最大并發(fā)操作數(shù) queue.maxConcurrentOperationCount = 1; // 串行隊列 // queue.maxConcurrentOperationCount = 2; // 并發(fā)隊列 // queue.maxConcurrentOperationCount = 8; // 并發(fā)隊列 // 3.添加操作 [queue addOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操作 NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程 } }]; [queue addOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操作 NSLog(@"2---%@", [NSThread currentThread]); // 打印當前線程 } }]; [queue addOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操作 NSLog(@"3---%@", [NSThread currentThread]); // 打印當前線程 } }]; [queue addOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操作 NSLog(@"4---%@", [NSThread currentThread]); // 打印當前線程 } }]; }
執(zhí)行結(jié)果為順序執(zhí)行1,1,2,2,3,3,4,4
如果將maxConcurrentOperationCount改為2, 則并并發(fā)執(zhí)行, 這里就不再驗證
-
NSOperation的操作依賴設置
/** * 操作依賴, 操作2依賴操作1 * 使用方法:addDependency: */ - (void)addDependency { // 1.創(chuàng)建隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.創(chuàng)建操作 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操作 NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程 } }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操作 NSLog(@"2---%@", [NSThread currentThread]); // 打印當前線程 } }]; // 3.添加依賴 [op2 addDependency:op1]; // 讓op2 依賴于 op1呆盖,則先執(zhí)行op1拖云,在執(zhí)行op2 // 4.添加操作到隊列中 [queue addOperation:op1]; [queue addOperation:op2]; }
-
NSOperation 提供了
queuePriority
屬性, 新建操作的默認優(yōu)先級是Normal// 優(yōu)先級的取值 typedef NS_ENUM(NSInteger, NSOperationQueuePriority) { NSOperationQueuePriorityVeryLow = -8L, NSOperationQueuePriorityLow = -4L, NSOperationQueuePriorityNormal = 0, NSOperationQueuePriorityHigh = 4, NSOperationQueuePriorityVeryHigh = 8 };
-
線程間通信示例
/** * 線程間通信 */ - (void)communication { // 1.創(chuàng)建隊列 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; // 2.添加操作 [queue addOperationWithBlock:^{ // 異步進行耗時操作 for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操作 NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程 } // 回到主線程 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // 進行一些 UI 刷新等操作 for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操作 NSLog(@"2---%@", [NSThread currentThread]); // 打印當前線程 } }]; }]; }
-
-
RunLoop
-
什么是RunLoop
RunLoop是通過內(nèi)部維護的事件循環(huán)來對事件進行管理的一個對象
-
為什么main函數(shù)不會退出
UIApplicationMain函數(shù)內(nèi)部默認開啟了主線程的RunLoop,并執(zhí)行了一段無限循環(huán)的代碼
-
RunLoop的數(shù)據(jù)結(jié)構(gòu)
NSRunLoop(Foundation)
是CFRunLoop(CoreFoundation)
的封裝应又,提供了面向?qū)ο蟮腁PI
RunLoop 相關(guān)的主要涉及五個類:CFRunLoop
:RunLoop對象-
CFRunLoopMode
:運行模式-
kCFRunLoopDefaultMode
:默認模式宙项,主線程是在這個運行模式下運行 -
UITrackingRunLoopMode
:跟蹤用戶交互事件(用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他Mode影響) -
UIInitializationRunLoopMode
:在剛啟動App時第進入的第一個 Mode株扛,啟動完成后就不再使用 -
GSEventReceiveRunLoopMode
:接受系統(tǒng)內(nèi)部事件尤筐,通常用不到 -
kCFRunLoopCommonModes
:偽模式汇荐,不是一種真正的運行模式,是同步Source/Timer/Observer到多個Mode中的一種解決方案
蘋果對外開放的主要有
kCFRunLoopDefaultMode
和kCFRunLoopCommonModes
一個比較常見的問題:滑動tableView時盆繁,定時器還會生效嗎掀淘?
默認情況下RunLoop運行在kCFRunLoopDefaultMode
下,而當滑動tableView時油昂,RunLoop切換到UITrackingRunLoopMode
革娄,而Timer是在kCFRunLoopDefaultMode
下的,就無法接受處理Timer的事件冕碟。 怎么去解決這個問題呢拦惋?把Timer添加到UITrackingRunLoopMode
上并不能解決問題,因為這樣在默認情況下就無法接受定時器事件了鸣哀。
所以我們需要把Timer同時添加到UITrackingRunLoopMode
和kCFRunLoopDefaultMode
上架忌。
那么如何把timer同時添加到多個mode上呢?就要用到NSRunLoopCommonModes
了[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
-
CFRunLoopSource
:輸入源/事件源CFRunLoopTimer
:定時源CFRunLoopObserver
:觀察者
-
RunLoop常見問題
- (void)test { NSLog(@"1"); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"2"); [[NSRunLoop currentRunLoop] run]; [self performSelector:@selector(test) withObject:nil afterDelay:10]; NSLog(@"3"); }); NSLog(@"4"); } - (void)test { NSLog(@"5"); }
輸出順序? 答案是: 1423, 5不會執(zhí)行, 原因是runloop開啟時應該有任務執(zhí)行mode中應該有item才行, 否則會退出, 所以應調(diào)整成如下代碼
dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"2"); //先添加任務 [self performSelector:@selector(test) withObject:nil afterDelay:10]; //再開啟循環(huán) [[NSRunLoop currentRunLoop] run]; NSLog(@"3"); });
-
* 如何創(chuàng)建一個常駐線程
```objc
@autoreleasepool {
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
```
* 怎樣保證子線程數(shù)據(jù)更新回到主線程更新UI時不影響滑動操作
答: 將更新UI的任務添加到主線程的**NSDefaultRunLoopMode** 上執(zhí)行即可, 這樣主線程將會在用戶停止滑動之后由UITrackingRunLoopMode切換到NSDefaultRunLoopMode之后更新UI
```objc
[self performSelectorOnMainThread:@selector(reloadData)
withObject:nil waitUntilDone:NO
modes:@[NSDefaultRunLoopMode]];
```
-
自旋鎖與互斥鎖
-
自旋鎖
當任務被另一個線程鎖定是, 嘗試執(zhí)行的線程會進入等待(不會休眠), 等上一個線程解除鎖定時, 立即執(zhí)行下一個線程任務,
優(yōu)點: 因為自旋鎖不會引起調(diào)用者睡眠我衬,所以不會進行線程調(diào)度叹放,CPU時間片輪轉(zhuǎn)等耗時操作。所有如果能在很短的時間內(nèi)獲得鎖挠羔,自旋鎖的效率遠高于互斥鎖
缺點: 自旋鎖一直占用CPU井仰,他在未獲得鎖的情況下,一直運行--自旋破加,所以占用著CPU俱恶,如果不能在很短的時 間內(nèi)獲得鎖,這無疑會使CPU效率降低范舀。自旋鎖不能實現(xiàn)遞歸調(diào)用
常用自旋鎖: atomic, dispatch_semaphore_t
-
互斥鎖
當上一個線程鎖定時, 下一個嘗試執(zhí)行的線程會進入休眠, 等上一個線程解除鎖定, 下一個線程自動喚醒然后執(zhí)行任務
常用互斥鎖: NSLock, NSCondition, @ synchronized
-
3. UI
-
UIView與CALayer的關(guān)系
UIView為CALayer提供內(nèi)容合是,以及負責處理觸摸等事件,參與響應鏈
CALayer負責顯示內(nèi)容contents -
事件傳遞與響應者鏈
-
事件傳遞過程
觸摸事件發(fā)生后, 系統(tǒng)會將事件加入到UIApplication管理的事件隊列
UIApplication會從事件隊列中取出最前面的事件锭环,并將事件分發(fā)下去以便處理聪全,通常 先發(fā)送事件給應 用的主窗口 keyWindow,主窗口再把事件發(fā)送給rootViewController
rootViewController再把事件發(fā)送給他的根View辅辩,然后會在View的層次結(jié)構(gòu)中找到一個最合適的視圖來處 理觸摸事件, 找到合適的視圖控件后难礼,就會調(diào)用視圖控件的touches方法來做具體的事件處理, 注意如果
-
找到合適的視圖控件后,就會調(diào)用視圖控件的touches方法來做具體的事件處理
touchesBegan touchesMoved touchesEnded
-
響應者鏈, 找到最合適處理事件的view
- 判斷當前view是否能夠接收觸摸事件
- 判斷當前的點玫锋,是否在當前view中
- 如果點在當前的view中 遍歷當前view的所有子view蛾茉,遍歷的過程是從后往前遍歷的 如果找到子控件,返回子控件 如果沒有子控件滿足條件撩鹿,返回當前view
// hitTest:withEvent:方法的處理流程如下: // ? hitTest:withEvent:會忽略隱藏谦炬、不和用戶交互的、透明度小于0.01的視圖 // ? 首先調(diào)用當前視圖的pointInside:withEvent:方法判斷觸摸點是否在當前視圖內(nèi); // ? 若返回NO,則hitTest:withEvent:返回nil; // ? 若返回YES,則向當前視圖的所有子視圖(subviews)發(fā)送hitTest:withEvent:消息节沦,所有 // 子視圖的遍歷順序是從top到bottom吧寺,即從subviews數(shù)組的末尾向前遍歷,直到有子視圖 // 返回非空對象或者全部子視圖遍歷完畢; // ? 若第一次有子視圖返回非空對象,則hitTest:withEvent:方法返回此對象窜管,處理結(jié)束; // ? 如所有子視圖都返回非,則hitTest:withEvent:方法返回自身(self)稚机。 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ //1 判斷當前view,是否可以接收事件 if (!self.userInteractionEnabled || self.alpha <= 0.01 || self.hidden) { return nil; } //2 判斷點擊的點获搏,是否在當前view中 if (![self pointInside:point withEvent:event]) { return nil; } //3 從后往前遍歷當前view的子view赖条,是否是最合適的view,如果不是返回self NSInteger count = self.subviews.count; for (NSInteger i =count-1;i>=0 ; i--) { UIView *childView = self.subviews[i]; //把當前坐標系的點常熙,轉(zhuǎn)換成子view坐標系的點 CGPoint subPoint = [self convertPoint:point toView:childView]; //尋找最合適的view // //判斷點是否在子控件中 if ([self pointInside:subPoint withEvent:event]) { UIView *fitView = [childView hitTest:subPoint withEvent:event]; if (fitView) { return fitView; } } } return self; }
-
-
圖像顯示原理
-
CPU工作
- Layout: 布局
- Display: 繪制
- Prepare: 圖片解碼
- Commit: 提交位圖
-
GPU工作
頂點著色纬乍,圖元裝配,cell光柵化裸卫,片段著色仿贬,片段處理
-
-
滾動視圖優(yōu)化:
-
CPU角度
- 對象創(chuàng)建銷毀,調(diào)整, 對于開銷大的對象進行優(yōu)化處理
- 預排版: 布局計算, 文本計算, 采用緩存高度等
- 預渲染: 文本圖片異步繪制等
-
GPU角度
考慮是有有不必要的CPU渲染
是否有太多的離屏渲染
是否有圖層的混合操作(透明度盡量都不要使用)
view的層次結(jié)構(gòu)是否合理
-
cell光柵化處理
// 光柵化 layer.shouldRasterize = true layer.rasterizationScale = UIScreen.mainScreen().scale // 異步繪制 layer.drawsAsynchronously = true
-
-
什么是離屏渲染(Off-Screen Rendering)
與之對應的就是On-Screen Rendering(在屏渲染), GPU的渲染操作主要用于顯示當前屏幕緩沖去進行的操作, 而離屏渲染指的是GPU在當前屏幕緩沖區(qū)外新開辟緩沖區(qū)進行渲染操作
-
離屏渲染什么時候會觸發(fā)?
答案: 圓角, 蒙版, 陰影, 等
4. OC語言特性
-
分類category
分類作用: 聲明私有方法, 給已存在類擴展方法(實例方法, 類方法, 協(xié)議, 屬性), 屬性需要借助運行時的關(guān)聯(lián)對象, 不能直接添加屬性
分類特點: 運行時決議
-
擴展
聲明私有屬性,聲明方法(沒什么意義)墓贿,聲明私有成員變量
-
代理
代理是一種設計模式茧泪,以@protocol形式體現(xiàn),一般是一對一傳遞聋袋。
一般以weak關(guān)鍵詞以規(guī)避循環(huán)引用队伟。 -
通知
使用觀察者模式來實現(xiàn)的用于跨層傳遞信息的機制。傳遞方式是一對多的幽勒。
-
KVO
-
實現(xiàn)原理 :
KVO的實現(xiàn)依賴于Objective-C強大的runtime嗜侮,KVO的底層實現(xiàn)是監(jiān)聽setter方法。當觀察某對象A時啥容,KVO動態(tài)機制會動態(tài)創(chuàng)建一個A類的子類NSKVONotifying_A锈颗,并為這個新的子類重寫父類的屬性的setter方法, 方法內(nèi)容如下
[super setAge:age]; ` [self willChangeValueForKey:@"age"]; [self didChangeValueForKey:@"age"]; ///其中后面兩個方法會調(diào)用 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
-
作用:
能夠監(jiān)聽某個對象屬性值的改變
-
-
KVC
通過Key值直接訪問對象屬性,或者給屬性賦值的操作, 常用操作valueforkey: / setValue: forKey:
底層執(zhí)行機制: (屬性name舉例子)
- 程序優(yōu)先調(diào)用屬性setName: 方法,代碼通過setter方法完成設置咪惠。
- 如果沒有屬性name, 會按照_name击吱,_iskey,key硝逢,iskey的順序搜索成員變量
- 如果以上都沒有會調(diào)用setValue:forUndefinedKey: 方法拋出錯誤
5 RunTime
RunTime是OC語言底層運行原理, OC代碼最終都要通過runtime去調(diào)用,
-
常見的runtime應用如下
-
通過交換方法可以實現(xiàn)攔截系統(tǒng)方法, 添加自己的需求
//常用方法 class_getClassMethod 獲取類方法 class_getInstanceMethod 獲取實例方法 method_exchangeImplementations 方法交換 在load方法中執(zhí)行狡猾
-
const char* name = "rylsj"; - (void) setNick:(NSString *)nick { objc_setAssociatedObject(self, &name, nick, OBJC_ASSOCIATION_COPY_NONATOMIC); } - (NSString*) nick { return objc_getAssociatedObject(self, &name); }
-
- 獲取屬性列表
-
- (void) rylsj_AddMethod { //"v@:@": v表示void, @表示id姨拥, :表示 SEL class_addMethod([self.persion class], @selector(run:), (IMP)runMethod, "v@:@"); } void runMethod(id self, SEL _cmd, NSString* rylsj) { NSLog(@"%@", rylsj); } //調(diào)用 if ([self.persion respondsToSelector:@selector(run:)]) { [self.persion performSelector:@selector(run:) withObject:@"66 rylsj"]; } else { NSLog(@"方法沒有實現(xiàn)!渠鸽!"); }
-
-
OC 消息轉(zhuǎn)發(fā)機制 (文中代碼有錯誤, 可以c)
首先根據(jù)receiver對象的isa指針獲取它對應的class
優(yōu)先在class的cache查找message方法叫乌,如果找不到,再到
methodLists查找如果沒有在class找到徽缚,再到super_class查找
一旦找到message這個方法憨奸,再依據(jù)receiver 中的self 指針找到當前的對象,調(diào)用當前對象的具體實現(xiàn)的方法(IMP),然后傳遞參數(shù),調(diào)用實現(xiàn)方法。
-
當對象收到無法解讀的消息后, 就會啟動"消息轉(zhuǎn)發(fā)(Message forwarding)"機制, 程序員可以通過消息轉(zhuǎn)發(fā)告訴對象應該如何處理位置消息, 過程如下:
-
系統(tǒng)會調(diào)用
resolveInstanceMethod
方法, 如果是類方法則會調(diào)用resolveClassMethod
, 在這個方法中我們可以增加調(diào)用方法的實現(xiàn), 我們可以通過一個Person類來實踐一下@interface Person : NSObject - (void)run; @end @implementation Person ///要添加的方法 void run (id self, SEL _cmd) { NSLog(@"人跑"); } //在這個方法中添加自己的方法實現(xiàn) + (BOOL)resolveInstanceMethod:(SEL)sel { if(sel == @selector(run)){ //關(guān)于生成簽名的類型"v@:"解釋一下凿试。每一個方法會默認隱藏兩個參數(shù)排宰,self似芝、_cmd,self代表方 法調(diào)用者板甘,_cmd代表這個方法的SEL党瓮,簽名類型就是用來描述這個方法的返回值、參數(shù)的盐类,v代表 返回值為void寞奸,@表示self,:表示_cmd在跳。 class_addMethod(self, sel, (IMP)run, "v@:"); return YES; } return [super resolveInstanceMethod:sel]; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; Person *person = [Person new]; [person run]; } @end //調(diào)用結(jié)果打印: "人跑"
如果未實現(xiàn)以上方法, 則會進入步驟二
-
forwardingTargetForSelector:
調(diào)用在這個方法中可以返回你需要轉(zhuǎn)發(fā)消息的對象, 這里我們可以新建一個對象Car來演示, 在Person中不是現(xiàn)方案一中的resolveInstanceMethod方法 改成實現(xiàn)forwardingTargetForSelector方法 如下:
@interface Car : NSObject - (void)run; @end @implementation Car - (void)run { NSLog(@"車跑"); } @end @implementation Person - (id)forwardingTargetForSelector:(SEL)aSelector { return [Car new]; } //執(zhí)行結(jié)果: 車跑
通過forwardingTargetForSelector把消息轉(zhuǎn)發(fā)給我們認為合適的對象去執(zhí)行, 如果此步驟未實現(xiàn), 則會進入下一步
-
通過
forwardInvocation
函數(shù) 設置我們自己生成的函數(shù)簽名和對象//生成簽名 - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { NSString *sel = NSStringFromSelector(selector); if([sel isEqualToString:@"run"]) { return [NSMethodSignature signatureWithObjCTypes:"v@:"]; } return [super methodSignatureForSelector:selector]; } //設置 - (void)forwardInvocation:(NSInvocation *)invocation { SEL selector = [invocation selector]; //新建需要轉(zhuǎn)發(fā)消息的對象 Car *car = [[Car alloc] init]; if([car respondsToSelector:selector]){ [invocation invokeWithTarget:car]; } } //執(zhí)行結(jié)果: 車跑
以上是當前類未實現(xiàn)方法的三種不就措施, 調(diào)用順序從前往后, 如果實現(xiàn)了第一種, 后面就不會執(zhí)行以此類推, 如果都沒實現(xiàn)程序就會crash
-
6. 內(nèi)存
-
內(nèi)存布局, 從代碼區(qū)到棧區(qū)地址一次從低到高
- 棧區(qū)(stack): 方法調(diào)用枪萄,局部變量等,是連續(xù)的猫妙,高地址往低地址擴展, 系統(tǒng)自動管理
- 堆區(qū)(heap): 通過alloc等分配的對象瓷翻,是離散的,低地址往高地址擴展割坠,需要我們手動控制
- 未初始化數(shù)據(jù)(bss): 未初始化的全局變量等
- 已初始化數(shù)據(jù)(data): 已初始化的全局變量等
- 代碼段(text): 程序代碼 (常量區(qū))
-
static關(guān)鍵字的作用
-
用于修飾存儲類型使之成為靜態(tài)存儲類型
在函數(shù)內(nèi)定義的靜態(tài)局部變量齐帚,該變量存在內(nèi)存的靜態(tài)區(qū),所以即使該函數(shù)運行結(jié)束韭脊,靜態(tài)變量的值不會被銷毀童谒,函數(shù)下次運行時能仍用到這個值。 在函數(shù)外定義的靜態(tài)變量——靜態(tài)全局變量沪羔,該變量的作用域只能在定義該變量的文件中饥伊,不能被其他文件通過extern引用。
-
用于修飾鏈接屬性使之成為內(nèi)部鏈接屬性
靜態(tài)函數(shù)只能在聲明它的源文件中使用蔫饰。
-
-
const關(guān)鍵字
-
聲明常變量琅豆,使得指定的變量不能被修改。
const int a = 5;/*a的值一直為5篓吁,不能被改變*/ const int b; b = 10;/*b的值被賦值為10后茫因,不能被改變*/ const int *ptr; /*ptr為指向整型常量的指針,ptr的值可以修改杖剪,但不能修改其所指向的值*/ int *const ptr;/*ptr為指向整型的常量指針冻押,ptr的值不能修改,但可以修改其所指向的值*/ const int *const ptr;/*ptr為指向整型常量的常量指針盛嘿,ptr及其指向的值都不能修改*/
-
-
修飾函數(shù)形參洛巢,使得形參在函數(shù)內(nèi)不能被修改,表示輸入?yún)?shù)
int fun(const int a);或int fun(const char *str);
-
修飾函數(shù)返回值次兆,使得函數(shù)的返回值不能被修改稿茉。
const char *getstr(void);使用:const *str= getstr(); const int getint(void); 使用:const int a =getint();
-
iOS 得內(nèi)存管理機制?
采用的是引用計數(shù)的方式進行內(nèi)存管理, MRC下需要用戶手動管理引用計數(shù), ARC下, 系統(tǒng)禁用retain,release,retainCount,autorelease等關(guān)鍵字
-
自動釋放池
在當次runloop將要結(jié)束的時候調(diào)用objc_autoreleasePoolPop漓库,并push進來一個新的AutoreleasePool, AutoreleasePoolPage是以棧為結(jié)點通過雙向鏈表的形式組合而成恃慧,是和線程一一對應的。內(nèi)部屬性有parent渺蒿,child對應前后兩個結(jié)點痢士,thread對應線程 ,next指針指向棧中下一個可填充的位置蘸嘶。
實現(xiàn)原理:
編譯器會將 @autoreleasepool {} 改寫為:
void * ctx = objc_autoreleasePoolPush; {} objc_autoreleasePoolPop(ctx);
-
objc_autoreleasePoolPush:
把當前next位置置為nil良瞧,即哨兵對象,然后next指針指向下一個可入棧位置,
AutoreleasePool的多層嵌套训唱,即每次objc_autoreleasePoolPush,實際上是不斷地向棧中插入哨兵對象挚冤。 -
objc_autoreleasePoolPop:
根據(jù)傳入的哨兵對象找到對應位置况增。給上次push操作之后添加的對象依次發(fā)送release消息⊙档玻回退next指針到正確的位置澳骤。
-
-
循環(huán)引用場景
-
代理(delegate)引起的相互循環(huán)引用
解決方案: 聲明地阿里是,使用weak關(guān)鍵字修飾
-
NSTimer引起的循環(huán)引用
解決方案: 在合適的時候進行invalidate 并將指針指向nil
-
Block引起的循環(huán)引用
對block中使用的self進行弱化 __weak typeof(self) weakSelf = self;
注意: 并不是所有block都會造成循環(huán)引用, 只有被強引用了的block才會產(chǎn)生循環(huán)引用
-
-
Block的內(nèi)存問題
-
Block的三種形式
-
全局Block(_NSConcreteGlobalBlock), 存儲在已初始化的數(shù)據(jù)區(qū)(.data)
不使用外部變量的是全局Block, 如下:
NSLog(@"%@",[^{ NSLog(@"globalBlock"); } class]); //輸出: __NSGlobalBlock__
-
棧Block(_NSConcreteStackBlock), 存儲在棧區(qū)(Stack)
使用外部變量但是未進行copy操作的Block是棧Block, 如下:
NSInteger num = 10; NSLog(@"%@",[^{ NSLog(@"stackBlock:%zd",num); } class]); //輸出: __NSStackBlock__
-
堆Block(_NSConcreteMallocBlock), 存儲在堆區(qū)(Heap)
對棧Block進行copy操作,就是堆block澜薄,而對全局Block進行copy为肮,仍是全局Block, 如下:
//對堆Block copy操作, 依然是全局Block void (^globalBlock)(void) = ^{ NSLog(@"globalBlock"); }; NSLog(@"%@",[globalBlock class]); //輸出: __NSGlobalBlock__ //對棧Block進行copy,是堆Block NSInteger num = 10; void (^mallocBlock)(void) = ^{ NSLog(@"stackBlock:%zd",num); }; NSLog(@"%@",[mallocBlock class]); //輸出: __NSMallocBlock__
注意: 對棧Block copy之后,并不代表著棧Block就消失了肤京,左邊的mallock是堆Block颊艳,右邊被copy的仍是棧Block
- (void)testWithBlock:(dispatch_block_t)block { block(); dispatch_block_t tempBlock = block; NSLog(@"%@,%@",[block class],[tempBlock class]); } //輸出: __NSStackBlock__,__NSMallocBlock__
-
-
7. 數(shù)據(jù)存儲
-
iOS中常用數(shù)據(jù)持久化方式
-
NSUserDefaults, 適用于輕量數(shù)據(jù)存儲
偏好設置會將所有數(shù)據(jù)保存到同一個文件中。即preference目錄下的一個以此應用包名來命名的plist文件忘分。
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:@"jack" forKey:@"firstName"]; [defaults setInteger:10 forKey:@"Age"]; ///現(xiàn)在可不手動調(diào)用同步方法 [defaults synchronize];
-
文件寫入本地(數(shù)組, 字典, 字符串等) 如下:
NSString *filePath = [[self getDocumentPath] stringByAppendingString:@"fileTest.txt"]; NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"798293@qq.com", @"email", @"787907@qq.com", @"emailDisplay", nil]; [dictionary writeToFile:filePath atomically:YES];
-
對象歸檔
需要對象實現(xiàn)NSCoding協(xié)議
NSString *filePath = [[self getDocumentPath] - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:_name forKey:kAddressCardName]; } - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { _name = [aDecoder decodeObjectForKey:kAddressCardName]; } //存儲 [NSKeyedArchiver archiveRootObject:obj toFile:filePath];
-
數(shù)據(jù)庫(SQLite, CoreData)
-
SQLite 常用函數(shù)
sqlite3_open() //創(chuàng)建并打開數(shù)據(jù)庫連接 sqlite3_exec() //執(zhí)行數(shù)據(jù)庫操作(crud)
-
-
* SQLite常用語句
* DDL(Data Definition Language) 數(shù)據(jù)庫定義語句
```sql
--create創(chuàng)表,
create table t_student (id integer, name text, age inetger, score real);
--drop刪除表
drop table if exists t_student;
```
* DML(Data Manipulation Language) 數(shù)據(jù)庫操作語句
```sql
--insert棋枕、
insert into t_student (name, age) values (‘wg’, 10);
--update、
update t_student set name = ‘jack’, age = 20;
--delete
delete from t_student;
```
* DQL(Data Query Language) 數(shù)據(jù)庫查詢語句
```sql
-- select
-- 查指定字段
select name, age from t_student;
-- 查全部
select * from t_student ;
-- 查數(shù)量
select count (age) from t_student ;
```
* 常用關(guān)鍵字: where妒峦,order by重斑,group by和having等
```sql
--where關(guān)鍵字示例
delete from t_student where age <= 10 or age > 30 ;
select * from t_student where age > 10 ;
-- 排序
select * from t_student order by age desc
-- limit條件限制
select * from t_student limit 4, 8 ;
```
* 別名
```sql
-- select 字段1 別名 , 字段2 別名 , … from 表名 別名 ;
select name myname, age myage from t_student;
```
8. 音視頻開發(fā)
-
音頻
-
音效播放 (AVFoundation), 支持acc , wav 等格式, 特征: 比較短 30秒以內(nèi)
//創(chuàng)建音效 NSString *path = [[NSBundle mainBundle]pathForResource:"music.acc" ofType:nil]; NSURL *url = [NSURL fileURLWithPath:path]; AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &soundID); //播放音效, 靜音無振動 AudioServicesPlaySystemSound(soundID); //靜音有震動 //AudioServicesPlayAlertSound(soundID);
-
音樂播放 (AVAudioPlayer: 只能播放本地音樂, 不支持網(wǎng)絡媒體)
//1.創(chuàng)建一個音樂對象 NSString *path = [[NSBundle mainBundle]pathForResource:@"music.mp3" ofType:nil]; NSURL *url = [NSURL fileURLWithPath:path]; AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil]; //2. 準備, 播放 [audioPlayer prepareToPlay]; //播放 [audioPlayer play];
-
音頻錄制(AVAudioRecorder)
//保存路徑 NSURL *url = [NSURL fileURLWithPath:[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]stringByAppendingPathComponent:@"recoder001.wav"]]; //錄音參數(shù)設置: AVSampleRateKey采樣率 AVNumberOfChannelsKey音頻通道數(shù), AVLinearPCMBitDepthKey線性音頻深度, NSMutableDictionary *settings = [NSMutableDictionary dictionary]; // 音頻采樣率 settings[AVSampleRateKey] = @(8000.0); //創(chuàng)建音頻錄音機 AVAudioRecorder *recorder = [[AVAudioRecorder alloc]initWithURL:url settings:settings error:&error]; //錄音 [recorder record]; //停止錄音 [self.recorder stop];
網(wǎng)絡媒體流播放(AVPlayer, 見下文視頻播放描述)
-
-
視頻
-
幾種視頻播放
首先在iOS平臺使用播放視頻,可用的選項一般有這四個肯骇,他們各自的作用和功能如下:
使用環(huán)境 優(yōu)點 缺點 MPMoviePlayerController MediaPlayer 簡單易用, 自帶頁面 不可定制 AVPlayerViewController AVKit 簡單易用, 自帶頁面 不可定制 AVPlayer AVFoundation 可定制度高窥浪,功能強大 不支持流媒體 IJKPlayer IJKMediaFramework 定制度高,支持流媒體播放 使用稍復雜 -
AVPlayer
-
播放音頻
NSURL *playUrl = [NSURL URLWithString:@"http://www.xxx.com/music.mp3"]; self.player = [[AVPlayer alloc] initWithURL:playUrl]; [self.player play];//播放 [self.player pause];//暫停 self.player.rate = 1.5//倍速
播放視頻 需要創(chuàng)建顯示層AVPlayerLayer
-
-
-
直播
-
主播端:
獲取音視頻授權(quán)
-
配置采樣參數(shù)
- 音頻: 碼率和采樣率
- 視頻: 分辨率, 幀率, 碼率
-
采集數(shù)據(jù)
- 音頻采集: AVAudioSession
- 視頻采集: GPUImageVideoCamera
-
編碼
- 音頻編碼: 軟編碼(h.264, mpeg等,第三方SDK)/硬編碼(AudioToolbox, 原生)
- 視頻編碼: 軟編碼(mp3, acc等, 第三方sdk)/硬編碼(VideoToolbox, 原生)
-
推流
- 音視頻封裝(flv或者TS格式)
- 協(xié)議(RTMP, HLS, FLV)
- 數(shù)據(jù)推送
-
服務器端
- 數(shù)據(jù)分發(fā)(CDN)
- 截屏
- 錄制
- 轉(zhuǎn)碼
-
用戶端
- 拉流: RTMP, HLS, FLV
- 解碼: 軟解碼和硬解碼
- 播放: IJKPlayer 開源播放器播放
直播常見問題 請參考文章 iOS開發(fā)之移動直播技術(shù)秒開笛丙、直播優(yōu)化經(jīng)驗漾脂、直播問題解析、直播知識解惑
-
9. iOS開發(fā)中的動畫
-
核心動畫(CoreAnimation)
-
核心動畫的分類(CAAnimation類的繼承結(jié)構(gòu))
CAAnimation有三個子類: CAAnimationGroup(組動畫), CAPropertyAnimation(屬性動畫), CATranstion(轉(zhuǎn)場動畫), 其中屬性動畫又分為: CABasicAnimation(基礎(chǔ)動畫)和CAKeyframeAnimation(關(guān)鍵幀動畫)
-
-
基礎(chǔ)動畫