AFN2.x
+ (instancetype)manager {
return [[AFHTTPRequestOperationManager alloc] initWithBaseURL:nil];
}
- (instancetype)initWithBaseURL:(NSURL *)url {
self = [super init];
if (!self) {
return nil;
}
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url;
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
if (self.baseURL.host) {
self.reachabilityManager = [AFNetworkReachabilityManager managerForDomain:self.baseURL.host];
} else {
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
}
self.operationQueue = [[NSOperationQueue alloc] init];
return self;
}
afn2.x 用单利AFHTTPRequestOperationManager对象请求
manager对象中 self.operationQueue
#pragma mark -
- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = self.responseSerializer;
operation.shouldUseCredentialStorage = self.shouldUseCredentialStorage;
operation.credential = self.credential;
operation.securityPolicy = self.securityPolicy;
[operation setCompletionBlockWithSuccess:success failure:failure];
return operation;
}
#pragma mark -
- (AFHTTPRequestOperation *)GET:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[self.operationQueue addOperation:operation];
return operation;
}
我们调用一个网络请求方法 会返回一个 AFHTTPRequestOperation对象 继承关系如下AFHTTPRequestOperation : AFURLConnectionOperation : NSOperation
我们看到我们每一个请求都返回一个自定义的Operation对象并且吧这个对象add到了self.operationQueue. 这样就就会自动调用自定义的Operation的star方法..
- (void)start {
[self.lock lock];
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]) {
//设置为startImmediately YES 请求发出,回调会加入到主线程的 Runloop 下,RunloopMode 会默认为 NSDefaultRunLoopMode
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
for (NSString *runLoopMode in self.runLoopModes) {
//把connection和outputStream注册到当前线程runloop中去,只有这样,才能在这个线程中回调
[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];
});
if ([self isCancelled]) {
[self finish];
}
}
在自定义的Operation的 start方法中会 利用runloop 创建一个常驻异步线程 ,并在常驻线程创建一个NSURLConnection对象
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
这个就是经典的afn线程保活.
总结
1afn2.x初始化的时候 创建了一个self.operationQueue = [[NSOperationQueue alloc] init];
2通过单利AFHTTPRequestOperationManager 调用网络请求方法都会创建一个自定义的 Operation对象并且把 Operation对象 加到self.operationQueue
3自定的Operation被加到self.operationQueue中Operation的start方法会自动执行 在start方法中创建了常驻线程
4在常驻线程中创建了一个NSURLConnection并把自己(Operation)作为connection的delgate
5 connection的代理方法都是常驻线程中回调的
以下方法是connection的代理方法 收到数据拼接数据
借鉴涂耀辉
//拼接获取到的数据
- (void)connection:(NSURLConnection __unused *)connection
didReceiveData:(NSData *)data
{
NSUInteger length = [data length];
while (YES) {
NSInteger totalNumberOfBytesWritten = 0;
//如果outputStream 还有空余空间
if ([self.outputStream hasSpaceAvailable]) {
//创建一个buffer流缓冲区,大小为data的字节数
const uint8_t *dataBuffer = (uint8_t *)[data bytes];
NSInteger numberOfBytesWritten = 0;
//当写的长度小于数据的长度,在循环里
while (totalNumberOfBytesWritten < (NSInteger)length) {
//往outputStream写数据,系统的方法,一次就写一部分,得循环写
numberOfBytesWritten = [self.outputStream write:&dataBuffer[(NSUInteger)totalNumberOfBytesWritten] maxLength:(length - (NSUInteger)totalNumberOfBytesWritten)];
//如果 numberOfBytesWritten写入失败了。跳出循环
if (numberOfBytesWritten == -1) {
break;
}
//加上每次写的长度
totalNumberOfBytesWritten += numberOfBytesWritten;
}
break;
}
//出错
if (self.outputStream.streamError) {
//取消connection
[self.connection cancel];
//调用失败的方法
[self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError];
return;
}
}
//主线程回调下载数据大小
dispatch_async(dispatch_get_main_queue(), ^{
self.totalBytesRead += (long long)length;
if (self.downloadProgress) {
self.downloadProgress(length, self.totalBytesRead, self.response.expectedContentLength);
}
});
}
这个方法看起来长,其实容易理解而且简单,它只做了3件事:
给outputStream拼接数据,具体如果拼接,大家可以读注释自行理解下。
如果出错则调用:connection:didFailWithError:也就是网络请求失败的代理,我们一会下面就会讲。
在主线程中回调下载进度。
6 connection代理方方,网络结束
//完成了调用
- (void)connectionDidFinishLoading:(NSURLConnection __unused *)connection {
//从outputStream中拿到数据 NSStreamDataWrittenToMemoryStreamKey写入到内存中的流
self.responseData = [self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
//关闭outputStream
[self.outputStream close];
//如果响应数据已经有了,则outputStream置为nil
if (self.responseData) {
self.outputStream = nil;
}
//清空connection
self.connection = nil;
[self finish];
}
7调用自己(自定的Operation)的私有方法 - finish 改变自己的状态
- (void)finish {
self.state = AFOperationFinishedState;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
});
}
8 改变状态isFinished 这里主动触发KVO
NSOperationQueue 是用KVO方式侦听 NSOperation 状态的改变
在该方法里统一发 KVO 通知给 NSOperationQueue,以判断这个任务当前是否已完成,完成的任务需要在队列中除去并释放。
这个方法改变state的时候,并且发送了KVO。大家了解NSOperationQueue就知道,如果对应的operation的属性finnished被设置为YES,则代表当前operation结束了,会把operation从队列中移除,并且调用operation的completionBlock
- (void)setState:(AFOperationState)state {
//判断从当前状态到另一个状态是不是合理,在加上现在是否取消。。大神的框架就是屌啊,这判断严谨的。。一层层
if (!AFStateTransitionIsValid(self.state, state, [self isCancelled])) {
return;
}
[self.lock lock];
//拿到对应的父类管理当前线程周期的key
NSString *oldStateKey = AFKeyPathFromOperationState(self.state);
NSString *newStateKey = AFKeyPathFromOperationState(state);
//发出KVO
[self willChangeValueForKey:newStateKey];//@"isFinished"
[self willChangeValueForKey:oldStateKey];
_state = state;
[self didChangeValueForKey:oldStateKey];
[self didChangeValueForKey:newStateKey];
[self.lock unlock];
}
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:
return @"state";
}
}
9接下来就是自己的(自定义的Operation)的CompletionBlock方法了.在这里我们最终完成的我们 网络请求block的回调
- (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"
self.completionBlock = ^{
dispatch_async(http_request_operation_processing_queue(), ^{
if (self.error) {
if (failure) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
NSError *error = nil;
id responseObject = [self.responseSerializer responseObjectForResponse:self.response data:self.responseData error:&error];
if (error) {
self.error = error;
if (failure) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
if (success) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
success(self, responseObject);
});
}
}
}
});
};
#pragma clang diagnostic pop
}
- (void)setCompletionBlock:(void (^)(void))block {
[self.lock lock];
if (!block) {
[super setCompletionBlock:nil];
} else {
__weak __typeof(self)weakSelf = self;
[super setCompletionBlock:^ {
__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();
});
dispatch_group_notify(group, queue, ^{
[strongSelf setCompletionBlock:nil];
});
}];
}
[self.lock unlock];
}
以上是afn2.x网络请求的主体结构 中间还有很多细节处处彰显着大神的🐂B
总结一下经典问题afn2.x为什么需要一个常驻线程
参考涂耀辉
首先如果我们用NSURLConnection,我们为了获取请求结果有以下三种选择:
1在主线程调异步接口
2每一个请求用一个线程,对应一个runloop,然后等待结果回调。
3只用一条线程,一个runloop,所有结果回调在这个线程上。
1试想如果我们所有的请求都在主线程中异步调用,好像没什么不可以?那为什么AF不这么做呢...在这里有两点原因(楼主个人总结的,有不同意见,欢迎讨论):
第一是,如果我们放到主线程去做,势必要这么写:
[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]
这样NSURLConnection的回调会被放在主线程中NSDefaultRunLoopMode
中,这样我们在其它类似UITrackingRunLoopMode
模式下,我们是得不到网络请求的结果的,这显然不是我们想要的,那么我们势必需要调用:
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
把它加入NSRunLoopCommonModes
中,试想如果有大量的网络请求,同时回调回来,我们的回调是在主线程回调的,比如拼接data,当然我们可以在回调中创建异步线程拼接data,频繁的创建线程同样影响我们的UI体验了。
另外一点原因是,如果我们请求数据返回,势必要进行数据解析,解析成我们需要的格式,那么这些解析都在主线程中做,给主线程增加额外的负担。
又或者我们回调回来开辟一个新的线程去做数据解析,那么我们有n个请求回来开辟n条线程带来的性能损耗,以及线程间切换带来的损耗,是不是一笔更大的开销。
所以综述两点原因,我们并不适合在主线程中回调。
2每一个请求都开辟一个线程,让connection的回调在新开辟的异步线程中回调.貌似没问题,但是新开辟的线程也得保活connection的才能在这个线程回调啊, 处理回调一条异步线程就够了干嘛要多个呢