美文网首页
AFNetworking源码分析(一)

AFNetworking源码分析(一)

作者: mrChan1234 | 来源:发表于2017-11-22 17:53 被阅读0次

    AFNetworking作为iOS开发中最常用的网络请求框架之一,其内部实现究竟是怎么一步一步实现的呢?现在我们来大致分析下其内部源码,由于本人水平有限,说错了的地方请大家积极指正,谢谢!

    以3.0.0的版本为例,其源码结构如下:


    源码结构.png

    1.NSURLSession文件夹中的类专门用于网络通信,其中最核心的类是AFURLSessionManager,其中的AFHTTPSessionManager这个类是前者的子类,顾名思义,AFHTTPSessionManager这个类是专门用于Http请求,

    2.Reachability中的AFNetworkRechabilityManager专门用于检测网络变化

    3.Security用于网络安全策略

    4.Serilization中的两个类专门用于请求头、响应头的处理
    其类与类之间的关系如下图所示:


    类与类之间关联.png
    AF工作流程 .png

    可以看到AFSessionManager中实现的最核心的类是Apple提供的系统类NSURLSession和NSURLSessionDataTask,这两个类提供了网络请求所需要的全部,由于我们最常使用的类是AFHTTPSessionManager,在进行网络请求时,我们一般使用一个AFHTTPSessionManager的实例变量来进行请求,如下代码所示:

       AFHTTPSessionManager *httpClient = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"www.baidu.com"]];
        httpClient.requestSerializer = [AFHTTPRequestSerializer serializer];
        httpClient.responseSerializer = [AFHTTPResponseSerializer serializer];
        [httpClient POST:@""
              parameters:@{}
                 success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                     //获取responseObject的数据,在主线程做UI刷新操作
                 } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {   
      }];
    

    首先设置基地址:

    - (instancetype)initWithBaseURL:(NSURL *)url
               sessionConfiguration:(NSURLSessionConfiguration *)configuration
    {
        self = [super initWithSessionConfiguration:configuration];
        if (!self) {
            return nil;
        }
        //http://www.baidu.com 确保完整路径的基地址
        // 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];
        return self;
    }
    

    这里设置了一个requestSerializer和responseSerializer,这两个类的作用是设置请求头、响应头相关的信息,主要用于传输不同类型的文件并设置不同的contentType等等,我们进入requestSerializer看下源码看具体做了什么:

    - (instancetype)init {
        self = [super init];
        if (!self) {
            return nil;
        }
        //编码
        self.stringEncoding = NSUTF8StringEncoding;
        //设置请求头
        self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];
        // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
        NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
        [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            float q = 1.0f - (idx * 0.1f);
            [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];
            *stop = q <= 0.5f;
        }];
        //服务器接收语言
        [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];
        //用户代理
        NSString *userAgent = nil;
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
    #if TARGET_OS_IOS
        //获取请求设备的相关信息 用户代理
        // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
        userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
    #elif TARGET_OS_WATCH
        // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
        userAgent = [NSString stringWithFormat:@"%@/%@ (%@; watchOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[WKInterfaceDevice currentDevice] model], [[WKInterfaceDevice currentDevice] systemVersion], [[WKInterfaceDevice currentDevice] screenScale]];
    #elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
        userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]];
    #endif
    #pragma clang diagnostic pop
        if (userAgent) {
            if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
                NSMutableString *mutableUserAgent = [userAgent mutableCopy];
                if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) {
                    userAgent = mutableUserAgent;
                }
            }
            [self setValue:userAgent forHTTPHeaderField:@"User-Agent"];
        }
    
        // HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
        self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
    
        self.mutableObservedChangedKeyPaths = [NSMutableSet set];
        for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
            if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
                [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
            }
        }
        return self;
    }
    

    这些信息主要是和后台人员沟通确定的,比如一般的后台请求,我们要设置AcceptableContentType为applicaiont/json,注明接受类型是已经被后台人员转好的json,在回调中我们就可以直接获取到json数据,而不用去进行转换, 有的时候,我们在勤奋求的时候,报错"Request failed: unacceptable content-type",就需要在请求头的contentype列表里面添加一些指定的contentType.
    其次就是主体的请求代码:

    [httpClient POST:@""
              parameters:@{}
                 success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                     //获取responseObject的数据,在主线程做UI刷新操作
                 } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                     
                 }];
    
    

    我们点进去看看,可以看到所有的请求方法都是基于下面这个方法来构建的:

    /**
     构建网络请求:所有请求(POST、GET、DELETE、PATCH、HEAD)都基于这个方法来构建起来的
     @param method POST、GET、DELETE、PATCH、HEAD
     @param URLString 请求地址
     @param parameters 请求参数
     @param uploadProgress 上传进度回调
     @param downloadProgress 下载进度列表
     @param success 成功回调
     @param failure 失败回调
     @return NSURLSessionDataTask网络请求
     */
    - (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                           URLString:(NSString *)URLString
                                          parameters:(id)parameters
                                      uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                    downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                             success:(void (^)(NSURLSessionDataTask *, id))success
                                             failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
    

    我们重点来分析下这个方法里面的代码:

    // 根据请求地址、方法、参数构建网络请求NSMutableURLRequest
        NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    

    这里使用了requestSerializer 的私有方法 method、params来构建了一个NSMutableURLRequest,并设置了请求体,请求头信息等等:代码如下:

    - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                                   withParameters:(id)parameters
                                            error:(NSError *__autoreleasing *)error
    {
        NSParameterAssert(request);
    
        NSMutableURLRequest *mutableRequest = [request mutableCopy];
    
        ///设置请求头信息
        [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
            if (![request valueForHTTPHeaderField:field]) {
                [mutableRequest setValue:value forHTTPHeaderField:field];
            }
        }];
        NSString *query = nil;
        if (parameters) {
            if (self.queryStringSerialization) {
                NSError *serializationError;
                query = self.queryStringSerialization(request, parameters, &serializationError);
                if (serializationError) {
                    if (error) {
                        *error = serializationError;
                    }
                    return nil;
                }
            } else {
                switch (self.queryStringSerializationStyle) {
                    case AFHTTPRequestQueryStringDefaultStyle:
                        query = AFQueryStringFromParameters(parameters);
                        break;
                }
            }
        }
       //判断是否是GET/HEAD/DELETE方法, 对于GET/HEAD/DELETE方法,把参数加到URL后面,这也就是GET请求里面的参数会暴露在请求地址里面
        if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
            if (query) {
                mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
            }
        } else {
            // #2864: an empty string is a valid x-www-form-urlencoded payload
           //一般用于POST请求
            if (!query) {
                query = @"";
            }
            ///设置Content-Type HTTP头,告诉服务端body的参数编码类型
            if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
                [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
            }
            //设置请求体信息 将json字符串转化为NSData类型
            [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
        }
        return mutableRequest;
    }
    

    接下来的就是构架一个NSURLSessionDataTask:

    __block NSURLSessionDataTask *dataTask = nil;
        dataTask = [self dataTaskWithRequest:request
                              uploadProgress:uploadProgress
                            downloadProgress:downloadProgress
                           completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
    //成功失败回调
            if (error) {
                if (failure) {
                    failure(dataTask, error);
                }
            } else {
                if (success) {
                    success(dataTask, responseObject);
                }
            }
        }];  //使用这个方法构建的task 需要手动resume 才会启动任务
      ///使用NSURLSession和NSURLSessionDataTask请求数据
    - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                                   uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                 downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler {
        __block NSURLSessionDataTask *dataTask = nil;
        url_session_manager_create_task_safely(^{
        //这里使用了NSURLSession来实例化一个dataTask
            dataTask = [self.session dataTaskWithRequest:request];
        });
        //为task添加代理来传输数据等相关信息
        [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
        return dataTask;
    }
    
    ///给NSURLSessionDataTask对象实例设置Delegate。用于实时了解网络请求
    - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                    uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                  downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                 completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
    {
        AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
        delegate.manager = self;
        delegate.completionHandler = completionHandler;
        dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    //为dataTask设置代理来接受并处理数据
        [self setDelegate:delegate forTask:dataTask];
        delegate.uploadProgressBlock = uploadProgressBlock;
        delegate.downloadProgressBlock = downloadProgressBlock;
    }
    
    - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
                forTask:(NSURLSessionTask *)task
    {
        NSParameterAssert(task);
        NSParameterAssert(delegate);
       //加锁 避免同时修改数据,造成线程安全问题
        [self.lock lock];
        self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
        [delegate setupProgressForTask:task];
        [self addNotificationObserverForTask:task];
        [self.lock unlock];
    }
    
    ///这里使用KVO监测task的请求过程
    - (void)setupProgressForTask:(NSURLSessionTask *)task {
        __weak __typeof__(task) weakTask = task;
    
        self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
        self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
        [self.uploadProgress setCancellable:YES];
        [self.uploadProgress setCancellationHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask cancel];
        }];
        [self.uploadProgress setPausable:YES];
        [self.uploadProgress setPausingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask suspend];
        }];
        if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
            [self.uploadProgress setResumingHandler:^{
                __typeof__(weakTask) strongTask = weakTask;
                [strongTask resume];
            }];
        }
    
        [self.downloadProgress setCancellable:YES];
        [self.downloadProgress setCancellationHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask cancel];
        }];
        [self.downloadProgress setPausable:YES];
        [self.downloadProgress setPausingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask suspend];
        }];
    
        if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
            [self.downloadProgress setResumingHandler:^{
                __typeof__(weakTask) strongTask = weakTask;
                [strongTask resume];
            }];
        }
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
    
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
    
        [self.downloadProgress addObserver:self
                                forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                                   options:NSKeyValueObservingOptionNew
                                   context:NULL];
        [self.uploadProgress addObserver:self
                              forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                                 options:NSKeyValueObservingOptionNew
                                 context:NULL];
    }
    
    //监听下载和上传任务的数据并回调
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
        if ([object isKindOfClass:[NSURLSessionTask class]]) {
            if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
                self.downloadProgress.completedUnitCount = [change[@"new"] longLongValue];
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
                self.downloadProgress.totalUnitCount = [change[@"new"] longLongValue];
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
                self.uploadProgress.completedUnitCount = [change[@"new"] longLongValue];
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
                self.uploadProgress.totalUnitCount = [change[@"new"] longLongValue];
            }
        }
        else if ([object isEqual:self.downloadProgress]) {
            //下载回调
            if (self.downloadProgressBlock) {
                self.downloadProgressBlock(object);
            }
        }
        else if ([object isEqual:self.uploadProgress]) {
            if (self.uploadProgressBlock) {
            //上传回调
                self.uploadProgressBlock(object);
            }
        }
    }
    
    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data {
      //获取NSURLSessionDataTask代理对象
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
        //试用delegate追加数据
        [delegate URLSession:session dataTask:dataTask didReceiveData:data];
        //数据接受回调
        if (self.dataTaskDidReceiveData) {
            self.dataTaskDidReceiveData(session, dataTask, data);
        }
    }
    
    //接收数据
    - (void)URLSession:(__unused NSURLSession *)session
              dataTask:(__unused NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {
        [self.mutableData appendData:data];
    }
    
    @interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
    

    AFURLSessionManagerTaskDelegate接管了NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate的各种回调,然后做内部数据处理,。这也是第三方网络请求框架的重点,让网络请求更加易用,好用。
    当然,由于本人技术有限,很多东西没理解透彻,如果有错误之处,欢迎大家指正,共同进步,后续的深入理解会继续写文章大家一起谈论,如果亲觉着这篇文章对于理解AFN有帮助的话,请下方点赞,谢谢!

    -------作者:mrChan1234

    相关文章

      网友评论

          本文标题:AFNetworking源码分析(一)

          本文链接:https://www.haomeiwen.com/subject/wecovxtx.html