發一條記錄的流程(而且能夠同時發多條記錄)html
其餘產品/技術需求(影響到設計因此也加在這)面試
無論你在何處工做,構建些什麼,用何種編程語言,在軟件開發上,一直伴隨你的那個不變真理是什麼?—— 《Head First 設計模式》編程
維護兩種隊列,暫且稱爲一級隊列和二級隊列。數據模型的設計和看到的界面(業務)是對應的,思路比較天然。如今我稱之爲針對實現設計。優缺點咱們後面陳述。設計模式
主要分爲兩層:markdown
二者最主要的區別:多線程
計A每一處設計都是面向業務的,沒有抽象;設計B中抽象層是把「上傳圖片功能」抽象了出來,能夠設計的徹底不知道業務(什麼叫不知道業務?)併發
B的設計有什麼優勢?app
上傳層其實還能夠抽象出不少層異步
最關鍵是實現上傳隊列,第一反應確定是考慮GCD或者NSOperation。這裏說下最終選擇NSOperation的緣由:async
對於NSOperation,須要瞭解如下幾件事:
先回憶一下,我們要幹什麼 —— 用一個隊列維護一個或者多個任務,每一個任務就是上傳一個圖片到七牛
先來解決「每一個任務就是上傳一個圖片到七牛」這件事,會想到3點要面臨的挑戰:
下面經過代碼來看下使用NSOperation自定義類,怎麼處理這三個問題,代碼來自Apple官方文檔《Concurrency Programming Guide》,能夠對照着看下我們的代碼。
@interface MyOperation : NSOperation { BOOL executing; BOOL finished; } - (void)completeOperation; @end @implementation MyOperation - (id)init { self = [super init]; if (self) { executing = NO; finished = NO; } return self; } - (BOOL)isConcurrent { // ------------2 return YES; } - (BOOL)isExecuting { // ------------2 return executing; } - (BOOL)isFinished { // ------------2 return finished; } - (void)start { // ------------1 // Always check for cancellation before launching the task. if ([self isCancelled]) // ------------3 { // Must move the operation to the finished state if it is canceled. [self willChangeValueForKey:@"isFinished"]; finished = YES; [self didChangeValueForKey:@"isFinished"]; return; } // If the operation is not canceled, begin executing the task. [self willChangeValueForKey:@"isExecuting"]; [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil]; executing = YES; [self didChangeValueForKey:@"isExecuting"]; } - (void)main { // ------------1 @try { // Do the main work of the operation here. [self completeOperation]; } @catch(...) { // Do not rethrow exceptions. } } - (void)completeOperation { [self willChangeValueForKey:@"isFinished"]; // ------------4 [self willChangeValueForKey:@"isExecuting"]; executing = NO; finished = YES; [self didChangeValueForKey:@"isExecuting"]; [self didChangeValueForKey:@"isFinished"]; } @end
代碼解讀
剩餘一些細節
咱們知道,每一個Operation的main方法,確定是會併發運行的,而token的獲取其實只要獲取一次,就行了,因此,咱們使用了信號量dispatch_semaphore來確保只之執行一次token的請求。
而後,每張圖片上傳以前,會使用系統方法,作一次人臉識別、寫入一次Exif信息,這兩部都是很是佔用內存的。若是併發執行,頗有可能讓內容衝到必定高度而Out Of Memory,爲了不這個問題,一個是人臉識別只使用一張小圖進行識別(不超過640*640),而且對於這兩個過程,加鎖。(關於iOS裏幾種鎖的用法和優缺點,建議瞭解一下,面試特別愛問)
如今咱們已經知道一個任務如何實現了,只須要將NSOperation扔到NSOperation Queue中,就會自動執行了。併發數可使用NSOperation Queue的maxConcurrentOperationCount
來控制併發數。
考慮一個問題:何時往Queue裏添加NSOperation?(一次性全加入?仍是?)
- (void)setHTTPShouldHandleCookies:(BOOL)HTTPShouldHandleCookies { [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))]; _HTTPShouldHandleCookies = HTTPShouldHandleCookies; [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))]; } + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { if ([AFHTTPRequestSerializerObservedKeyPaths() containsObject:key]) { return NO; } return [super automaticallyNotifiesObserversForKey:key]; }