上一章讲解了AFNetworking 2.x的基本用法,主要从AFHTTPRequestOperationManager调用POST/GET方法开始通过requestSerilizer请求序列化,然后创建AFHTTPRequestOperation对象,使用operation对象进行网络请求,最后在设置operation结束后的completionBlock,内部包括responseSerilizer响应序列化。整个过程都是围绕operation对象的,可以说整个AFNetworking 2.x 网络库的核心就是AFHTTPRequestOperation&AFURLRequestOperation。本文就来梳理这个核心部分内容。
AFURLReuqestOperation&AFHTTPRequestOperation主要的工作分为两部分:
- 继承NSOPeration的常见方法:设置completionBlock, operation状态管理,start方法等等
- 实现NSURLConnetion的几个代理方法
1 completionBlock调用failure&success
在NSOperation中,completionBlock会在finished属性被设置成YES以后调用,并且调用的线程不一定是主线程,因此如果有耗时操作需要自己放到子线程中去完成。AFNetworking中关于completionBlock就是这样处理的,将completionBlock中的参数block方法加入到自定义的completionGroup中,然后将耗时操作success&failure放到自定义的completionQueue中。
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
// completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
//会触发父类的setCompletionBlock方法 -- 需要先看父类的方法 -- 见下面
self.completionBlock = ^{
//completionGroup会在父类的setCompletionBlock方法里面初始化,使用的公用的自定义的group
if (self.completionGroup) {
dispatch_group_enter(self.completionGroup);
}
//创建processing_queue,然后在这个queue里面异步调用
dispatch_async(http_request_operation_processing_queue(), ^{
if (self.error) {//如果有_responseSerializationError 或者其他的error
if (failure) {
//异步在completion group queue中调用 failure block
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
//直接在responseObject中调用(*)responseSerializer进行响应的解析
id responseObject = self.responseObject;
if (self.error) {//判断解析过程中是否有error发生
if (failure) {//如果有error发生,同上
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {//说明response 解析没有出错,到达真正的success
if (success) {//在completionGroup和queue中调用success block
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
success(self, responseObject);
});
}
}
}
//注意:上面的if else语句中最终只会有一个failure 或者 success加入都 dispatch_group中
if (self.completionGroup) {
dispatch_group_leave(self.completionGroup);
}
});
};
#pragma clang diagnostic pop
}
//这是父类AFURLConnectionOperation重写的NSOperation的completionBlock的setter方法,这个
//过程是加锁的
- (void)setCompletionBlock:(void (^)(void))block {
[self.lock lock];
//如果传入的block为nil,直接将completionBlock设置为nil
if (!block) {
[super setCompletionBlock:nil];
} else {
//使用weak strong引用的技巧,使用weakSelf是防止block应用operation对象,operation对象强引
//用这个completionBlock,在block执行过程中,将weakSelf 转化为强引用,是防止在block执行过程
//中,self对象被释放,因此这里会有循环引用,然后在最后group内的所有的block都执行完成以后,
//在自定义的queue中将completionBlock设置成nil,主动破解循环引用。
__weak __typeof(self)weakSelf = self;
//如果block不为nil,就给设置completionblock,等operation执行完成以后执行以下的代码
[super setCompletionBlock:^ {
__strong __typeof(weakSelf)strongSelf = weakSelf;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
//使用了?:运算符,如果当前completionGroup自定义了会使用自定义的group,否则会用onceToken
//创建一个dispatch_group,这个 group 是所有的operation共用的,后面所有的operation的completionBlock设置为nil以及block的运行也与这个group有关!!!
dispatch_group_t group = strongSelf.completionGroup ?: url_request_operation_completion_group();
//completionQueue同理,如果自定义了就用自定义的否则会使用mainqueue
dispatch_queue_t queue = strongSelf.completionQueue ?: dispatch_get_main_queue();
#pragma clang diagnostic pop
//异步使用group queue 调用传入的block,这里所有的operation对象都会在同一个group中去异步执行block
dispatch_group_async(group, queue, ^{
//ps:在block中执行结束后只会有一个failure 或者 success block会被加入到group中异步执行
block();
});
//在传入的completionBlcok中使用了dispatch_group_enter &dispatch_group_leave方法,其中所有加入到queue的方法都会受到这个group的影响,这里等待所有进入group的任务都执行完了,再在自定义的queue中将NSOperation的completionBlock手动设置为nil,这里是在一个全局共用的自定义的queue中执行的。
dispatch_group_notify(group, url_request_operation_completion_queue(), ^{
[strongSelf setCompletionBlock:nil];
});
}];
}
[self.lock unlock];
}
上面的代码中,有一处值得注意的地方是 id responseObject = self.responseObject,会触发getter方法:
- (id)responseObject {
[self.lock lock];
if (!_responseObject && [self isFinished] && !self.error) {
NSError *error = nil;
// 调用responseSerializer方法解析response
self.responseObject = [self.responseSerializer responseObjectForResponse:self.response data:self.responseData error:&error];
if (error) {
self.responseSerializationError = error;
}
}
[self.lock unlock];
return _responseObject;
}
以上设置completionBlock代码中,值得学习的技巧:
1 在block中使用weakSelf,在block内容开始执行时候转化位strong类型,防止运行过程中释放,在block调用结束时,手动释放block,解除循环引用
__weak __typeof(self)weakSelf = self;
[super setCompletionBlock:^ {
__strong __typeof(weakSelf)strongSelf = weakSelf;
2 使用dispatch_once保证只会调用一次,多线程中常用的技巧,并且对于仅仅调用一次的方式使用static method来声明
static dispatch_group_t http_request_operation_completion_group() {
static dispatch_group_t af_http_request_operation_completion_group;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_http_request_operation_completion_group = dispatch_group_create();
});
return af_http_request_operation_completion_group;
}
3 自定义thread,让线程一直在后台运行的方法
①_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil]; 创建新线程
②在startPoint里面加入autoreleasepool。因为自定义线程里面没有主线程的autoreleasepool,需要手动添加。
③设置自定义线程的名称,方便跟踪调试
④给runloop添加一个MachPort信息,然后run runloop,使得线程保持在后台运行
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
注意前面提到的:completionBlock被执行的时机:completionBlock会在finished属性被设置成YES以后调用
2 自定义operation的状态管理
NSOperation->AFURLRequestOpertion ->AFHTTPRequestOperation是继承关系。犹豫NSOPerationQueue会通过KVO观察NSOperation的状态来判断operation是否finish&excuting&cancel等等,因此在子类创建自己的operation类需要自己用代码管理operation类的状态:isReady,isExecuting,isFinished,并且在状态改变时发送KVO。AFNetworking在AFURLReqeustOperation中进行operation的状态管理,同时增加了一个pause状态.
关于NSOperation state的文章可以参考 NSHipster
- (BOOL)isReady {
return self.state == AFOperationReadyState && [super isReady];
}
- (BOOL)isExecuting {
return self.state == AFOperationExecutingState;
}
- (BOOL)isFinished {
return self.state == AFOperationFinishedState;
}
- (BOOL)isPaused {
return self.state == AFOperationPausedState;
}
可以看出是通过枚举用于记录operation当前状态定义的状态机,然后使用@property (readwrite, nonatomic, assign) AFOperationState state
属性记录当前状态,通过属性的setter进行状态转移的控制。
typedef NS_ENUM(NSInteger, AFOperationState) {
AFOperationPausedState = -1,
AFOperationReadyState = 1,
AFOperationExecutingState = 2,
AFOperationFinishedState = 3,
};
- (void)setState:(AFOperationState)state {
//首先判断能否从当前状态转移到newState
if (!AFStateTransitionIsValid(self.state, state, [self isCancelled])) {
return;
}
[self.lock lock];
//为后面KVO做准备,设置key
NSString *oldStateKey = AFKeyPathFromOperationState(self.state);
NSString *newStateKey = AFKeyPathFromOperationState(state);
//注意:在operation实现中对于状态变化需要使用KVO通知给NSOperationQueue:finish,excuting,ready等等
[self willChangeValueForKey:newStateKey];
[self willChangeValueForKey:oldStateKey];
_state = state;
[self didChangeValueForKey:oldStateKey];
[self didChangeValueForKey:newStateKey];
[self.lock unlock];
}
//通过static方法来判断是否能: 状态A -> 状态B 返回值表示YES&NO
static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperationState toState, BOOL isCancelled) {
switch (fromState) {
case AFOperationReadyState:
switch (toState) {
case AFOperationPausedState:
case AFOperationExecutingState:
return YES;
case AFOperationFinishedState:
return isCancelled;
default:
return NO;
}
case AFOperationExecutingState:
switch (toState) {
case AFOperationPausedState:
case AFOperationFinishedState:
return YES;
default:
return NO;
}
case AFOperationFinishedState:
return NO;
case AFOperationPausedState:
return toState == AFOperationReadyState;
default: {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
switch (toState) {
case AFOperationPausedState:
case AFOperationReadyState:
case AFOperationExecutingState:
case AFOperationFinishedState:
return YES;
default:
return NO;
}
}
#pragma clang diagnostic pop
}
}
// 用于将状态属性->NSString 的方法
static inline NSString * AFKeyPathFromOperationState(AFOperationState state) {
switch (state) {
case AFOperationReadyState:
return @"isReady";
case AFOperationExecutingState:
return @"isExecuting";
case AFOperationFinishedState:
return @"isFinished";
case AFOperationPausedState:
return @"isPaused";
default: {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
return @"state";
#pragma clang diagnostic pop
}
}
}
对于新增加的paused状态,使用以下方法进行管理:
- (void)resume {
if (![self isPaused]) {//必须是paused状态才能resume
return;
}
[self.lock lock];
self.state = AFOperationReadyState;
[self start];//手动调用start方法
[self.lock unlock];
}
- (void)pause {
if ([self isPaused] || [self isFinished] || [self isCancelled]) {//必须excuting或者ready状态才能pause
return;
}
[self.lock lock];
if ([self isExecuting]) {
[self performSelector:@selector(operationDidPause) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
dispatch_async(dispatch_get_main_queue(), ^{
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
});
}
self.state = AFOperationPausedState;
[self.lock unlock];
}
状态管理总结:
1 创建枚举方法表示NSOperation的状态,至少包括excuting, finished状态。
2 新建static方法AFStateTransitionIsValid判断能否从:状态A->状态B,在此过程中,需要注意isCancelled状态
3 在state属性的setter方法setState中进行 状态改变,其中改变前后需要进行KVO需要通知相关状态的变化。
3 Operation主要的工作
首先看下自定义Operation的核心方法start
- (void)start {
[self.lock lock];
//在实现NSOperation的方法时,要经常判断operation是否cancel
if ([self isCancelled]) {
//在自定义的Thread中调用cancelConnection方法
[self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
} else if ([self isReady]) {
//设置state为excuting状态
self.state = AFOperationExecutingState;
//在自定义的线程中调用operationDidStart方法
[self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
}
[self.lock unlock];
}
//使用once创建一个线程
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
//创建一个线程以后就直接start
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
//自定义线程的enterPoint
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];//名称,方便调试
//自定义线程没有启动runloop,如果没有runloop,在线程运行完enterPoint方法以后,线程就会退出,这里希望线程长期存在,因此需要在runloop中加入MachPort,防止线程退出
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
- (void)operationDidStart {
[self.lock lock];
if (![self isCancelled]) {
//在对象的connection属性创建NSURLConnection对象,delegate是operation对象
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
for (NSString *runLoopMode in self.runLoopModes) {
//将当前operation中的connetion的delegate方法放到子线程中去执行
[self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
[self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
}
//启动connetion&outputStream
[self.outputStream open];
[self.connection start];
}
[self.lock unlock];
//全局消息AFNetworkingOperationDidStartNotification
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
});
}
NSURLConnecion在子线程运行过程中常见错误,delegate方法不会被调用——线程被销毁,因此有了自定义Thread&runloop
官方文档中关于NSURLConnetion的方法注释如下:
- scheduleInRunLoop:forMode:
Determines the run loop and mode that the connection uses to call methods on its delegate.
以上问题和runloop有关,可以参考: 孙源runloop视频, 深入理解RunLoop
4 Operation实现的NSURLConnetion的几个Delegate方法
当调用了start方法以后,就在自定义的子线程Thread中等待NSURLConnetion的delegate回调。
AFURLReuqestOperation实现了NSURLConnectionDelegate,NSURLConnectionDataDelegate方法
//一般用于HTTPS请求中的SSL/TSL认证,证书用的自签名证书
- (void)connection:(NSURLConnection *)connection
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if (self.authenticationChallenge) {
self.authenticationChallenge(connection, challenge);
return;
}
//如果需要认证的是server:host
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
//使用安全策略去判断是否信任(*),安全策略后面讲解
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
} else {
if ([challenge previousFailureCount] == 0) {
if (self.credential) {
[[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge];
} else {
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
}
} else {
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
}
}
}
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection __unused *)connection {
return self.shouldUseCredentialStorage;
}
//对于url重定向的情况delegate如何处理,如果是自身遇到重定向问题,直接继承operation,直接设置redirectResponse属性即可
- (NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)redirectResponse
{
if (self.redirectResponse) {
return self.redirectResponse(connection, request, redirectResponse);
} else {
return request;
}
}
//post请求中,向外提供progress情况,一般通过继承operation以后直接设置uploadProgress
- (void)connection:(NSURLConnection __unused *)connection
didSendBodyData:(NSInteger)bytesWritten
totalBytesWritten:(NSInteger)totalBytesWritten
totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
{
dispatch_async(dispatch_get_main_queue(), ^{
if (self.uploadProgress) {
self.uploadProgress((NSUInteger)bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}
});
}
//在connetion请求的response返回以后调用的第一个delegate方法,得到NSURLResponse ,NSHTTPResponse对象,可以获取responseHeaders,statusCode等信息
- (void)connection:(NSURLConnection __unused *)connection
didReceiveResponse:(NSURLResponse *)response
{
self.response = response;
}
//response data内容
- (void)connection:(NSURLConnection __unused *)connection
didReceiveData:(NSData *)data
{
NSUInteger length = [data length];
//直接将收到的data写到outputStream中,写入内存中
while (YES) {
NSInteger totalNumberOfBytesWritten = 0;
if ([self.outputStream hasSpaceAvailable]) {
const uint8_t *dataBuffer = (uint8_t *)[data bytes];
NSInteger numberOfBytesWritten = 0;
while (totalNumberOfBytesWritten < (NSInteger)length) {
numberOfBytesWritten = [self.outputStream write:&dataBuffer[(NSUInteger)totalNumberOfBytesWritten] maxLength:(length - (NSUInteger)totalNumberOfBytesWritten)];
if (numberOfBytesWritten == -1) {
break;
}
totalNumberOfBytesWritten += numberOfBytesWritten;
}
break;
} else {
//如果出错直接调用delegate的connection:didFailWithError:方法
//调用方法以后Once the delegate receives this message, it will receive no further messages for connection.
[self.connection cancel];
if (self.outputStream.streamError) {
[self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError];
}
return;
}
}
dispatch_async(dispatch_get_main_queue(), ^{
self.totalBytesRead += (long long)length;
//在mainQueue中调用downloadProgress方法,一般通过继承operation以后直接设置uploadProgress
if (self.downloadProgress) {
self.downloadProgress(length, self.totalBytesRead, self.response.expectedContentLength);
}
});
}
//当url请求成功完成以后会调用,获取了responseData数据,该数据会在completionBlock里面通过
//responeObject 的 getter方法调用
- (void)connectionDidFinishLoading:(NSURLConnection __unused *)connection {
self.responseData = [self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
[self.outputStream close];
if (self.responseData) {
self.outputStream = nil;
}
self.connection = nil;
[self finish];//改变connection状态
}
- (void)connection:(NSURLConnection __unused *)connection
didFailWithError:(NSError *)error
{
self.error = error;
[self.outputStream close];
if (self.responseData) {
self.outputStream = nil;
}
self.connection = nil;
[self finish];//改变connection状态
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
if (self.cacheResponse) {
return self.cacheResponse(connection, cachedResponse);
} else {
if ([self isCancelled]) {
return nil;
}
return cachedResponse;
}
}
这部分代码注释比较清晰,具体的调用流程如下:
①operationDidStart会打开outputStream
②connection:didReceiveData: delegate 方法中每次都往outputStream写数据
③写数据同时回调上层传进来的相应的downloadProgress block
④在connection:DidFinishLoading时,拿到responseData,关闭outputStream,清空ouputstream,设置operation状态。
⑤finished状态以后,会自动调用completionBlock,回调到上层
5 其他内容
① 后台运行使用setShouldExecuteAsBackgroundTaskWithExpirationHandler
② 锁 self.lock = [[NSRecursiveLock alloc] init]; ,在访问重要变量时,都会加锁
③ 为什么在completionBlock中使用dispatch_group系列方法?主要目的是等待加入group中的success&failure执行完成以后使用dispatch_group_notify方法setCompletionBlock:nil