发一条记录的流程(而且能够同时发多条记录)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]; }