AFNetworking核心类AFURLConnectionOperation的详解

本文深入剖析AFNetworking的核心类AFURLConnectionOperation,涵盖线程使用、状态机设计、NSURLConnectionDelegate实现、setCompleteBlock机制、批量请求操作、锁与序列化策略、AFHTTPRequestOperation扩展以及AFHTTPRequestOperationManager的便捷功能,旨在全面理解AFNetworking的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇我们了解了AFNetworking各个模块的功能,今天我们来了解一下AFNetworking源码的实现

我们先看看AFURLConnectionOperation   作为AFNetworking最核心的类到底有什么神奇之处!

1.线程

先来看看 NSURLConnection 发送请求时的线程情况,NSURLConnection 是被设计成异步发送的,调用了start方法后,NSURLConnection 会新建一些线程用底层的 CFSocket 去发送和接收请求,在发送和接收的一些事件发生后通知原来线程的Runloop去回调事件。
NSURLConnection 的同步方法 sendSynchronousRequest 方法也是基于异步的,同样要在其他线程去处理请求的发送和接收,只是同步方法会手动block住线程,发送状态的通知也不是通过 RunLoop 进行。
NSURLConnection发送有以下三种方式
  • 在主线程调异步接口
  • 在子线程调同步接口
  • 在子线程调异步接口
AFNetworking使用的是最后一种方式方法,AFNetworking内部相关线程大致的关系如下图所示
NSURLConnection是一个系统控件,所以我们可以把NSURLConnection当做一个黑盒,只管它的 start 和 callback 就行了。如果使用 AFHttpRequestOperationManager 的接口发送请求,这些请求会统一在一个 NSOperationQueue 里去发,所以多了上面 NSOperationQueue 的一个线程。

相关代码:
//-------------------------线程--------------------
/*
    子线程调用异步接口,子线程需要有 Runloop 去接收异步回调事件,这里也可以每个请求都新建一条
    带有 Runloop 的线程去侦听回调,但这一点好处都没有,既然是异步回调,除了处理回调内容,其他
    时间线程都是空闲可利用的,所有请求共用一个响应的线程就够了。
 */
//获取当前的NSRunLoop,子线程使用共同的Runloop
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
    @autoreleasepool {
        [[NSThread currentThread] setName:@"AFNetworking"];
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runLoop run];
    }
}
//创建新的子线程
+ (NSThread *)networkRequestThread {
    <span style="white-space:pre">	</span>  static NSThread *_networkRequestThread = nil;
    <span style="white-space:pre">	</span>  static dispatch_once_t oncePredicate;
    <span style="white-space:pre">	</span>  dispatch_once(&oncePredicate, ^{
      <span style="white-space:pre">	</span>  _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
       <span style="white-space:pre">	</span> [_networkRequestThread start];
    });  
    return _networkRequestThread;
}
//初始化NSURLConnection对象
- (instancetype)initWithRequest:(NSURLRequest *)urlRequest {
    NSParameterAssert(urlRequest);
    self = [super init];
    if (!self) {
		return nil;
    }
    _state = AFOperationReadyState;
    self.lock = [[NSRecursiveLock alloc] init];
    self.lock.name = kAFNetworkingLockName;
    self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes];
    self.request = urlRequest;  
    self.shouldUseCredentialStorage = YES;
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];
    return self;
}
//------------------------线程--------------------
//线程开始
- (void)start {
    //加锁,保护线程
    [self.lock lock];
    if ([self isCancelled]) {   //取消线程
        [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
    } else if ([self isReady]) {    //线程已准备
        self.state = AFOperationExecutingState;     //将线程调为执行状态
        
        [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
    }
    //线程执行完毕,解锁
    [self.lock unlock];
}
//线程已开始
- (void)operationDidStart {
    [self.lock lock];
    if (![self isCancelled]) {
        //创建链接对象
        self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];     
        //获取当前的NSRunLoop,用来接收异步回调事件
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        for (NSString *runLoopMode in self.runLoopModes) {
            //执行线程
            [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
            [self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
        }     
        //开始链接
        [self.connection start];
    }
    [self.lock unlock];   
    //回到主线程,发送线程开始通知
    dispatch_async(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
    });
}
//线程完成
- (void)finish {
    [self.lock lock];
    self.state = AFOperationFinishedState;
    [self.lock unlock];
    //发送通知,链接完成
    dispatch_async(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
    });
}
//线程取消
- (void)cancel {
    [self.lock lock];
    if (![self isFinished] && ![self isCancelled]) {
        [super cancel];

        if ([self isExecuting]) {
            [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
        }
    }
    [self.lock unlock];
}
//取消链接
- (void)cancelConnection {
    NSDictionary *userInfo = nil;
    if ([self.request URL]) {
        userInfo = [NSDictionary dictionaryWithObject:[self.request URL] forKey:NSURLErrorFailingURLErrorKey];
    }
    NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo];
    if (![self isFinished]) {      //处于链接状态,取消链接
        if (self.connection) {
            //取消链接
            [self.connection cancel];
            [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:error];
        } else {            //链接完成,则直接结束
            // Accomodate race condition where `self.connection` has not yet been set before cancellation
            self.error = error;
            [self finish];
        }
    }
}

2.状态机

继承 NSOperation 有个很麻烦的东西要处理,就是改变状态时需要发 KVO 通知,否则这个类加入 NSOperationQueue 不可用了。 NSOperationQueue 是用 KVO 方式侦听 NSOperation 状态的改变,以判断这个任务当前是否已完成,完成的任务需要在队列中除去并释放。
AFURLConnectionOperation 对此做了个状态机,统一搞定状态切换以及发 KVO 通知的问题,内部要改变状态时,就只需要类似 self.state = AFOperationReadyState 的调用而不需要做其他了,状态改变的 KVO 通知在 setState 里发出。
总的来说状态管理相关代码就三部分,一是限制一个状态可以切换到其他哪些状态,避免状态切换混乱,二是状态 Enum值 与 NSOperation 四个状态方法的对应,三是在 setState 时统一发 KVO 通知。
相关代码如下:
//-------------------------状态机
//该方法的作用:状态 Enum值 与 NSOperation 四个状态方法的对应
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
        }
    }
}
//NSOperation 状态的切换:限制一个状态可以切换到其他哪些状态,避免状态切换混乱
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
    }
}
//-------------------------状态机
//NSOperationQueue 是用KVO方式侦听 NSOperation 状态的改变
//在该方法里统一发 KVO 通知给 NSOperationQueue,以判断这个任务当前是否已完成,完成的任务需要在队列中除去并释放。
- (void)setState:(AFOperationState)state {
    if (!AFStateTransitionIsValid(self.state, state, [self isCancelled])) {
        return;
    }  
    [self.lock lock];
    NSString *oldStateKey = AFKeyPathFromOperationState(self.state);
    NSString *newStateKey = AFKeyPathFromOperationState(state);
    [self willChangeValueForKey:newStateKey];
    [self willChangeValueForKey:oldStateKey];
    _state = state;
    [self didChangeValueForKey:oldStateKey];
    [self didChangeValueForKey:newStateKey];
    [self.lock unlock];
}
还有其他相关代码:-setState:, -isPaused:, -isReady:, -isExecuting:, -isFinished:.

3.NSURLConnectionDelegate

处理 NSURLConnection Delegate 的内容不多,代码也是按请求回调的顺序排列下去,十分易读,主要流程就是接收到响应的时候打开 outputStream,接着有数据过来就往 outputStream 写,在上传/接收数据过程中会回调上层传进来的相应的callback,在请求完成回调到 connectionDidFinishLoading 时,关闭 outputStream,用 outputStream 组装 responseData 作为接收到的数据,把 NSOperation 状态设为 finished,表示任务完成,NSOperation 会自动调用 completeBlock,再回调到上层。
相关代码如下:
//使用NSURLConnection时,用该方法检查证书的有效性
- (void)connection:(NSURLConnection *)connection
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{}
/*
 connection: willSendRequest: redirectResponse:
 这个方法在请求将要被发送出去之前会调用
 返回值是一个NSURLRequest,就是那个真正将要被发送的请求
 第二个参数request就是被重定向处理过后的请求
 第三个参数<span style="font-family: Arial, Helvetica, sans-serif;">redirectResponse</span><span style="font-family: Arial, Helvetica, sans-serif;">是触发重定向请求的响应包.默认是支持跳转的。</span>
 */
- (NSURLRequest *)connection:(NSURLConnection *)connection
             willSendRequest:(NSURLRequest *)request
            redirectResponse:(NSURLResponse *)redirectResponse
{
    if (self.redirectResponse) {
        return self.redirectResponse(connection, request, redirectResponse);
    } else {
        return request;
    }
}
//上传数据
- (void)connection:(NSURLConnection __unused *)connection
   didSendBodyData:(NSInteger)bytesWritten
 totalBytesWritten:(NSInteger)totalBytesWritten
totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
{
    //上传数据过程中回调上层传进来的相应的callback
    if (self.uploadProgress) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.uploadProgress((NSUInteger)bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
        });
    }
}
//代理对象接收到响应的时候打开 outputStream
- (void)connection:(NSURLConnection __unused *)connection
didReceiveResponse:(NSURLResponse *)response
{
    self.response = response; 
    [self.outputStream open];
}
//有数据过来时,往outputStream写数据
- (void)connection:(NSURLConnection __unused *)connection
    didReceiveData:(NSData *)data
{
    NSUInteger length = [data length];
    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;
        }     
        if (self.outputStream.streamError) {
            [self.connection cancel];
            [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError];
            return;
        }
    }  
    //接收数据过程中会回调上层传进来的相应的callback
    dispatch_async(dispatch_get_main_queue(), ^{
        self.totalBytesRead += (long long)length;

        if (self.downloadProgress) {
            self.downloadProgress(length, self.totalBytesRead, self.response.expectedContentLength);
        }
    });
}
//请求完成回调到 connectionDidFinishLoading 时,关闭 outputStream
//用 outputStream 组装 responseData 作为接收到的数据,把 NSOperation 状态设为 finished,表示任务完成
- (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];
}

4.setCompleteBlock

AFNetworking重写NSOperation提供的setCompletionBlock,用于任务完成时回调传进来的block,并且实现消除循环引用。
在 NSOperation 的实现里,completionBlock 是 NSOperation 对象的一个成员,NSOperation 对象持有着 completionBlock,若传进来的 block 用到了 NSOperation 对象,或者 block 用到的对象持有了这个 NSOperation 对象,就会造成循环引用。这里执行完 block 后调用 [strongSelf setCompletionBlock:nil] 把 completionBlock 设成 nil,手动释放 self(NSOperation对象) 持有的 completionBlock 对象,打破循环引用。
相关代码如下:
<span style="font-size:14px;">//任务完成,回调block
- (void)setCompletionBlock:(void (^)(void))block {
    [self.lock lock];
    if (!block) {
        [super setCompletionBlock:nil];
    } else {
        //weakSelf是为了block不持有self,避免循环引用
        __weak __typeof(self)weakSelf = self;
        [super setCompletionBlock:^ {
            //再声明一个strongSelf是因为一旦进入block执行,就不允许self在这个执行过程中释放。block执行完后这个strongSelf会自动释放,没有循环引用问题。
            __strong __typeof(weakSelf)strongSelf = weakSelf;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            dispatch_group_t group = strongSelf.completionGroup ?: url_request_operation_completion_group();
            dispatch_queue_t queue = strongSelf.completionQueue ?: dispatch_get_main_queue();
#pragma clang diagnostic pop
            dispatch_group_async(group, queue, ^{
                //传入一个 block 作为任务执行完成时(state状态机变为finished时)的回调
                block();
            });
            /*
             循环引用:NSOperation持有completionBlock,若传进来的block 用到了 NSOperation 对象,
             或者 block 用到的对象持有了这个 NSOperation 对象,就会造成循环引用
             解决方法:
             消除循环引用,手动释放 self(NSOperation对象) 持有的 completionBlock 对象,打破循环引用
             */
            dispatch_group_notify(group, url_request_operation_completion_queue(), ^{
                [strongSelf setCompletionBlock:nil];
            });
        }];
    }
    [self.lock unlock];
}</span>

5.batchOfRequestOperations

这里额外提供了一个便捷接口,可以传入一组请求,在所有请求完成后回调 complionBlock,在每一个请求完成时回调 progressBlock 通知外面有多少个请求已完成
<span style="font-size:14px;">+ (NSArray *)batchOfRequestOperations:(NSArray *)operations
                        progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock
                      completionBlock:(void (^)(NSArray *operations))completionBlock
{
    //请求不存在,或者请求数为0,返回
    if (!operations || [operations count] == 0) {
        return @[[NSBlockOperation blockOperationWithBlock:^{
            dispatch_async(dispatch_get_main_queue(), ^{
                if (completionBlock) {
                    completionBlock(@[]);
                }
            });
        }]];
    }
    __block dispatch_group_t group = dispatch_group_create();
    //任务数为0时执行dispatch_group_notify的内容
    NSBlockOperation *batchedOperation = [NSBlockOperation blockOperationWithBlock:^{
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            if (completionBlock) {
                completionBlock(operations);
            }
        });
    }];
    //取出每一个请求(任务)
    for (AFURLConnectionOperation *operation in operations) {
        operation.completionGroup = group;
        void (^originalCompletionBlock)(void) = [operation.completionBlock copy];
        __weak __typeof(operation)weakOperation = operation;
        operation.completionBlock = ^{
            __strong __typeof(weakOperation)strongOperation = weakOperation;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            dispatch_queue_t queue = strongOperation.completionQueue ?: dispatch_get_main_queue();
#pragma clang diagnostic pop
            //异步执行任务
            dispatch_group_async(group, queue, ^{
                if (originalCompletionBlock) {
                    //任务完成后回调block
                    originalCompletionBlock();
                }
                NSUInteger numberOfFinishedOperations = [[operations indexesOfObjectsPassingTest:^BOOL(id op, NSUInteger __unused idx,  BOOL __unused *stop) {
                    return [op isFinished];
                }] count];

                //在每一个请求完成时回调 progressBlock 通知外面有多少个请求已完成。
                if (progressBlock) {
                    progressBlock(numberOfFinishedOperations, [operations count]);
                }

                dispatch_group_leave(group);        //类似release,任务数-1
            });
        };
        dispatch_group_enter(group);                //类似retain,任务数+1
        [batchedOperation addDependency:operation];
    }
    return [operations arrayByAddingObject:batchedOperation];
}</span>

6.锁、序列化、backgroundTask

锁 :AFURLConnectionOperation 有一把递归锁,在所有会访问/修改成员变量的对外接口都加了锁,因为这些对外的接口用户是可以在任意线程调用的,对于访问和修改成员变量的接口,必须用锁保证线程安全。
序列化:AFNetworking 的多数类都支持序列化,实现的是 NSSecureCoding 的接口,用 -decodeObjectOfClass:forKey: 方法,指定 Class保证序列化后的数据不被篡改,若不指定 Class,-decode 出来的对象可能不是原来的对象,有潜在风险。
backgroundTask:这里提供了setShouldExecuteAsBackgroundTaskWithExpirationHandler 接口,决定APP进入后台后是否继续发送接收请求,并在后台执行时间超时后取消所有请求。在 dealloc 里需要调用 [application endBackgroundTask:] ,告诉系统这个后台任务已经完成,不然系统会一直让你的APP运行在后台,直到超时。


7.AFHTTPRequestOperation

AFHTTPRequestOperation 继承了 AFURLConnectionOperation,实现的功能比较少,主要多了responseSerializer,暂停下载断点续传,以及提供接口请求成功失败的回调接口。理解了AFURLConnectionOperation,就会觉得AFHTTPRequestOperation比较简单,所以具体的就不写了。

- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation,id responseObject))success

                              failure:(void (^)(AFHTTPRequestOperation *operation,NSError *error))failure;


8.AFHTTPRequestOperationManager

AFHTTPRequestOperationManager封装了AFNetworking其他功能的各个模块,如AFHTTPRequestSerializer(请求序列化),AFHTTPResponseSerializer(响应序列化)AFSecurityPolicy(安全策略),AFNetworkReachabilityManager(可达性),封装了HTTP请求所相关的代码
并且将所有的请求添加到同一个NSOperationQueue请求队列里。

并且封装 HTTP 请求的常见方式,GET / POST / PUT / DELETE / PATCH……

 
  
NSURLConnection相关的代码就解析到这里。下一篇讲AFNetworking其他功能模块!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值