美文网首页
AFNetworking详解

AFNetworking详解

作者: Jey | 来源:发表于2020-12-24 20:45 被阅读0次

    AFNetworking3.0主要是对NSURLSession的封装,NSURLConnection是iOS9以前的

    NSURLSession,网络通信核心类

    • AFURLSessionManager
    • AFHTTPSessionManager
      AFHTTPSessionManager继承AFURLSessionManager

    序列化

    • < AFURLRequestSerialization> 协议
      -- AFHTTPRequestSerializer 根类
      -- AFJSONRequestSerializer
      -- AFPropertyListRequestSerializer

    • < AFURLResponseSerialization> 协议
      -- AFHTTPResponseSerializer 根类
      -- AFJSONResponseSerializer
      -- AFXMLParserResponseSerializerXMLParser
      -- AFXMLDocumentResponseSerializer (Mac OS X) 不适合我们的iOS
      -- AFPropertyListResponseSerializer plist
      -- AFImageResponseSerializer //国片
      -- AFCompoundResponseSerializer //混合

    1. 初始化session
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    
    /*
     1.调用父类的方法
     2.给url添加“/”
     3.给requestSerializer、responseSerializer设置默认值
     */
    - (instancetype)initWithBaseURL:(NSURL *)url
               sessionConfiguration:(NSURLSessionConfiguration *)configuration
    {
        //调用父类初始化方法
        self = [super initWithSessionConfiguration:configuration];
        if (!self) {
            return nil;
        }
    
        // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
        /*
         为了确保NSURL +URLWithString:relativeToURL: works可以正确执行,在baseurlpath的最后添加‘/’
         */
        //url有值且没有‘/’,那么在url的末尾添加‘/’
        if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
            url = [url URLByAppendingPathComponent:@""];
        }
        self.baseURL = url;
        //给requestSerializer、responseSerializer设置默认值
        self.requestSerializer = [AFHTTPRequestSerializer serializer];
        self.responseSerializer = [AFJSONResponseSerializer serializer];
    
        return self;
    }
    
    2. 设置了一些默认值,返回数据的格式之类
    /*
     1.初始化一个session
     2.给manager的属性设置初始值
     */
    - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
        self = [super init];
        if (!self) {
            return nil;
        }
    
        //设置默认的configuration,配置我们的session
        if (!configuration) {
            configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        }
    
        //持有configuration
        self.sessionConfiguration = configuration;
    
        // 设置为delegate的操作队列并发的线程数量1,也就是串行队列,返回数据
        self.operationQueue = [[NSOperationQueue alloc] init];
        self.operationQueue.maxConcurrentOperationCount = 1;
    
        /*
         -如果完成后需要做复杂(耗时)的处理,可以选择异步队列
         -如果完成后直接更新UI,可以选择主队列
         [NSOperationQueue mainQueue]
         */
        
        self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    
        //默认为json解析
        self.responseSerializer = [AFJSONResponseSerializer serializer];
    
        //设置默认证书 无条件信任证书https认证
        self.securityPolicy = [AFSecurityPolicy defaultPolicy];
    
    #if !TARGET_OS_WATCH
        //网络状态监听
        self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
    #endif
    
        //delegate= value taskid = key
        self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    
        //使用NSLock确保线程安全
        self.lock = [[NSLock alloc] init];
        self.lock.name = AFURLSessionManagerLockName;
    
        
        //异步的获取当前session的所有未完成的task。其实讲道理来说在初始化中调用这个方法应该里面一个task都不会有
        //后台任务重新回来初始化session,可能就会有先前的任务
        //https://github.com/AFNetworking/AFNetworking/issues/3499
        [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
            for (NSURLSessionDataTask *task in dataTasks) {
                [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
            }
    
            for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
                [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
            }
    
            for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
                [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
            }
        }];
    
        return self;
    }
    
    3. 创建一个NSMutableURLRequest
    //1.生成request,2.通过request成成task
    - (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
    {
        NSError *serializationError = nil;
        /*
         1.先调用AFHTTPRequestSerializer的requestWithMethod函数构建request
         2.处理request构建产生的错误 – serializationError
         //relativeToURL表示将URLString拼接到baseURL后面
    
         */
        NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
        if (serializationError) {
            if (failure) {
                //http://fuckingclangwarnings.com/#semantic
                //xcode忽略编译器的警告,diagnostic:诊断的
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
                //completionQueue不存在返回dispatch_get_main_queue
                dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                    failure(nil, serializationError);
                });
    #pragma clang diagnostic pop
            }
    
            return nil;
        }
    
        //此时的request已经将参数拼接在url后面
        __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);
                }
            }
        }];
    
        return dataTask;
    }
    

    给NSMutableURLRequest自带的属性赋值,将传入的参数进行编码处理等

    - (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                     URLString:(NSString *)URLString
                                    parameters:(id)parameters
                                         error:(NSError *__autoreleasing *)error
    {
        //断言如果nil,直接打印出来
        NSParameterAssert(method);
        NSParameterAssert(URLString);
    
        //我们传进来的是一个字符串,在这里它帮你转成url
        NSURL *url = [NSURL URLWithString:URLString];
    
        NSParameterAssert(url);
    
        NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
        //设置请求方式(get、post、put。。。)
        mutableRequest.HTTPMethod = method;
    
        //将request的各种属性遍历,给NSMutableURLRequest自带的属性赋值
        for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
            //给设置过得的属性,添加到request(如:timeout)
            if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
                //通过kvc动态的给mutableRequest添加value
                [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
            }
        }
    
        //将传入的参数进行编码,拼接到url后并返回 coount=5&start=1
        mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
        NSLog(@"request'''''''''%@",mutableRequest);
    
        return mutableRequest;
    }
    
    #pragma mark - AFURLRequestSerialization
    - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                                   withParameters:(id)parameters
                                            error:(NSError *__autoreleasing *)error
    {
        NSParameterAssert(request);
    
        NSMutableURLRequest *mutableRequest = [request mutableCopy];
    
        //给mutableRequest.headfiled赋值
        /*
          1.请求行(状态行):get,url,http协议1.1
         2.请求头:conttent-type,accept-language
         3.请求体:get/post get参数拼接在url后面 post数据放在body
        
         */
        [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 {
                //默认解析方式,dic-   count=5&start=1
                switch (self.queryStringSerializationStyle) {
                    case AFHTTPRequestQueryStringDefaultStyle:
                        //将parameters传入这个c函数
                        query = AFQueryStringFromParameters(parameters);
                        break;
                }
            }
        }
    
        //count=5&start=1
        NSLog(@"query:%@",query);
        //最后判断该request中是否包含了GET、HEAD、DELETE(都包含在HTTPMethodsEncodingParametersInURI)。因为这几个method的query是拼接到url后面的。而POST、PUT是把query拼接到http body中的。
        if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
            if (query && query.length > 0) {
                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
            if (!query) {
                query = @"";
            }
            //函数会判断request的Content-Type是否设置了,如果没有,就默认设置为application/x-www-form-urlencoded
            //application/x-www-form-urlencoded是常用的表单发包方式,普通的表单提交,或者js发包,默认都是通过这种方式
            if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
                [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
            }
            //设置请求体
            [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
        }
    
        return mutableRequest;
    }
    
    4. 通过request生成task
    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);
                }
            }
        }];
    
    5. 给task添加代理

    taskId作为key,delegate作为value,存储在一个字典中self.mutableTaskDelegatesKeyedByTaskIdentifier

     [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
    
    /*
     注意addDelegateForDataTask:这个函数并不是AFURLSessionManagerTaskDelegate的函数,而是AFURLSessionManager的一个函数。这也侧面说明了AFURLSessionManagerTaskDelegate和NSURLSessionTask的关系是由AFURLSessionManager管理的。
     */
    - (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
    {
        // 初始化delegate
        AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
        delegate.manager = self;
        delegate.completionHandler = completionHandler;
        
        /*
         taskidentifier=key delegate=value,确保task唯一
         taskDescription自行设置的,区分是否是当前的session创建的
         */
        dataTask.taskDescription = self.taskDescriptionForSessionTasks;
        //函数字面意思是将一个session task和一个AFURLSessionManagerTaskDelegate类型的delegate变量绑在一起,而这个绑在一起的工作是由我们的AFURLSessionManager所做。至于绑定的过程,就是以该session task的taskIdentifier为key,delegate为value,赋值给mutableTaskDelegatesKeyedByTaskIdentifier这个NSMutableDictionary类型的变量。知道了这两者是关联在一起的话,马上就会产生另外的问题 —— 为什么要关联以及怎么关联在一起?
        [self setDelegate:delegate forTask:dataTask];
        //设置回调块
        delegate.uploadProgressBlock = uploadProgressBlock;
        delegate.downloadProgressBlock = downloadProgressBlock;
    }
    

    为task设置关联的delegate,,放入self.mutableTaskDelegatesKeyedByTaskIdentifier

    - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
                forTask:(NSURLSessionTask *)task
    {
        //task和delegate都不能为空
        NSParameterAssert(task);
        NSParameterAssert(delegate);
    
        //加锁确保中间代码块是原子操作,线程安全
        [self.lock lock];
        //将delegate存入字典,以taskid作为key,说明每个task都有各自的代理
        self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
        //设置两个NSProgress的变量 - uploadProgress和downloadProgress,给session task添加了两个KVO事件
        [delegate setupProgressForTask:task];
        [self addNotificationObserverForTask:task];
        [self.lock unlock];
    }
    

    监听task的事件

    #pragma mark - NSProgress Tracking
    
    - (void)setupProgressForTask:(NSURLSessionTask *)task {
        __weak __typeof__(task) weakTask = task;
    
        //拿到上传下载期望的数据大小
        self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
        self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
        
         //设置这两个NSProgress对应的cancel、pause和resume这三个状态,正好对应session task的cancel、suspend和resume三个状态
        [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和progress添加kvo,监测进度大小
        [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];
    
        //fractionCompleted:任务已经完成的比例,取值为0~1
        [self.downloadProgress addObserver:self
                                forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                                   options:NSKeyValueObservingOptionNew
                                   context:NULL];
        [self.uploadProgress addObserver:self
                              forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                                 options:NSKeyValueObservingOptionNew
                                 context:NULL];
    }
    ```
    #####6. session的系统回调,拿到绑定的delegate做数据处理
    ```
    // 这里拼接data
    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {
    
    
    // 调用的是AFURLSessionManagerTaskDelegate的didReceiveData方法
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
        [delegate URLSession:session dataTask:dataTask didReceiveData:data];
    
        if (self.dataTaskDidReceiveData) {
            self.dataTaskDidReceiveData(session, dataTask, data);
        }
    }
    ```
    
    最终都会到这里
    ```
    /*
     task完成之后的回调,成功和失败都会回调这里
     函数讨论:
     注意这里的error不会报告服务期端的error,他表示的是客户端这边的error,比如无法解析hostname或者连不上host主机。
     */
    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
      
    
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
    
        // delegate may be nil when completing a task in the background
        // 如果task是在后台完成的,可能delegate会为nil
        if (delegate) {
            [delegate URLSession:session task:task didCompleteWithError:error];
    
            // 该task结束了,就移除对应的delegate
            [self removeDelegateForTask:task];
        }
        //自定义Block回调
        if (self.taskDidComplete) {
            self.taskDidComplete(session, task, error);
        }
    }
    ```
    
    在AFURLSessionManagerTaskDelegate中的方法中,数据正常返回的话,开启并行队列解析数据375行,block返回
    ```
    //AF实现的代理!被从urlsession那转发到这
    /*
     第一个是获取数据,将responseSerializer和downloadFileURL或data存到userInfo里面
     第二个是根据error是否为空值,做下一步处理
     */
    - (void)URLSession:(__unused NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
    
    
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
        //1)强引用self.manager,防止被提前释放;因为self.manager声明为weak,类似Block
        __strong AFURLSessionManager *manager = self.manager;
    
        __block id responseObject = nil;
        // 因为NSNotification这个类中本身有userInfo属性,可作为响应函数的参数
        // 不过我在AFNetworking源码中还未发现使用userInfo作为参数的做法,可能需要用户自己实现
        
        /**
         * userInfo中的key值例举如下:
         * AFNetworkingTaskDidCompleteResponseDataKey session 存储task获取到的原始response数据,与序列化后的response有所不同
         * AFNetworkingTaskDidCompleteSerializedResponseKey 存储经过序列化(serialized)后的response
         * AFNetworkingTaskDidCompleteResponseSerializerKey 保存序列化response的序列化器(serializer)
         * AFNetworkingTaskDidCompleteAssetPathKey 存储下载任务后,数据文件存放在磁盘上的位置
         * AFNetworkingTaskDidCompleteErrorKey 错误信息
         */
        //用来存储一些相关信息,来发送通知用的
        __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
        
        //存储responseSerializer响应解析对象
        userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
    
        //Performance Improvement from #2672
        //具体可以查看#issue 2672。这里主要是针对大文件的时候,性能提升会很明显
        NSData *data = nil;
        if (self.mutableData) {
            data = [self.mutableData copy];
            //We no longer need the reference, so nil it out to gain back some memory.
            self.mutableData = nil;
        }
        
        //继续给userinfo填数据
        ////如果downloadFileURL存在,即是下载任务就设置下载完成后的文件存储url到字典中
        if (self.downloadFileURL) {
            userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
        } else if (data) {
            //否则就设置对应的NSData数据到字典中
            userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
        }
        // 如果task出错了,处理error信息
        // 所以对应的观察者在处理error的时候,比如可以先判断userInfo[AFNetworkingTaskDidCompleteErrorKey]是否有值,有值的话,就说明是要处理error
        if (error) {
            userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
            // 这里用group方式来运行task完成方法,表示当前所有的task任务完成,才会通知执行其他操作
            // 如果没有实现自定义的completionGroup和completionQueue,那么就使用AFNetworking提供的私有的dispatch_group_t和提供的dispatch_get_main_queue内容
            dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                if (self.completionHandler) {
                    self.completionHandler(task.response, responseObject, error);
                }
                //主线程中发送完成通知
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
        } else {//在没有error时,会先对数据进行一次序列化操作,然后下面的处理就和有error的那部分一样了
            dispatch_async(url_session_manager_processing_queue(), ^{
                NSError *serializationError = nil;
                // 根据对应的task和data将response data解析成可用的数据格式,比如JSON serializer就将data解析成JSON格式
                responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
                // 注意如果有downloadFileURL,意味着data存放在了磁盘上了,所以此处responseObject保存的是data存放位置,供后面completionHandler处理。没有downloadFileURL,就直接使用内存中的解析后的data数据
                if (self.downloadFileURL) {
                    responseObject = self.downloadFileURL;
                }
                //写入userInfo
    
                if (responseObject) {
                    userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
                }
                 // 序列化的时候出现错误
    
                if (serializationError) {
                    userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
                }
                //回调结果
                //同理,在dispatch组中和特定队列执行回调块
                dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                    if (self.completionHandler) {
                        self.completionHandler(task.response, responseObject, serializationError);
                    }
                        //主线程发送通知
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                    });
                });
            });
        }
    #pragma clang diagnostic pop
    }
    
    ```
    注意这里的是通过主队列发送出去
    ```
    dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                    if (self.completionHandler) {
                        self.completionHandler(task.response, responseObject, serializationError);
                    }
                        //主线程发送通知
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                    });
                });
    ```
    
    #### 序列化
    requestSerialization:请求前的配置
    reponseSerialization:请求后
    
    ##### post上传文件-----------------------------------------------
    ```
    // 1. 使用AFHTTPSessionManager的接口
        AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
        NSDictionary *dic = @{@"businessType":@"CC_USER_CENTER",
                              @"fileType":@"image",
                              @"file":@"img.jpeg"
                              };
        [manager POST:@"http://xxx/api/demo/test/file" parameters:dic constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
            // 在这个block中设置需要上传的文件
            NSString *path = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"png"];
            // 将本地图片数据拼接到formData中 指定name
            [formData appendPartWithFileURL:[NSURL fileURLWithPath:path] name:@"file" error:nil];
            
    //             或者使用这个接口拼接 指定name和filename
    //                    NSData *picdata  =[NSData dataWithContentsOfFile:path];
    //                    [formData appendPartWithFileData:picdata name:@"image" fileName:@"image.jpg" mimeType:@"image/jpeg"];
            
        } progress:^(NSProgress * _Nonnull uploadProgress) {
            NSLog(@"progress --- %@",uploadProgress.localizedDescription);
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSLog(@"responseObject-------%@", responseObject);
            dispatch_async(dispatch_get_main_queue(), ^{
                
            });
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"Error-------%@", error);
        }];
    ```
    
    ```
    //构建一个multipartForm的request。并且通过`AFMultipartFormData`类型的formData来构建请求体
    - (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
                                                  URLString:(NSString *)URLString
                                                 parameters:(NSDictionary *)parameters
                                  constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                                                      error:(NSError *__autoreleasing *)error
    {
        NSParameterAssert(method);
        //method不能是get、head
        NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);
    
        NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];
        // 使用initWithURLRequest:stringEncoding:来初始化一个AFStreamingMultipartFormData变量
        // 主要是为了构建bodyStream
        __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];
    
        
        if (parameters) {
            for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
                NSData *data = nil;
                if ([pair.value isKindOfClass:[NSData class]]) {
                    data = pair.value;
                } else if ([pair.value isEqual:[NSNull null]]) {
                    data = [NSData data];
                } else {
                    //通常nslog打印会调用description,打印出来的是地址,但是可以重写description,来实现打印出我们想要的类型
                    data = [[pair.value description] dataUsingEncoding:self.stringEncoding];
                }
    
                if (data) {
                    // bodyStream构造最主要的部分就在这了(虽然后面requestByFinalizingMultipartFormData函数还会稍微处理一下)
                    // 根据data和name构建Request的header和body,后面详解
                    [formData appendPartWithFormData:data name:[pair.field description]];
                }
            }
        }
    // 参考上面的例子,其实还是往formData中添加数据
        if (block) {
            block(formData);
        }
    // 做最终的处理,比如设置一下MultipartRequest的bodyStream或者其特有的content-type等等,后面也会详解
        return [formData requestByFinalizingMultipartFormData];
    }
    ```
    1.使用AFStreamingMultipartFormData初始化formdata,里面是AFMultipartBodyStream的实例
    2. 把参数转成data,拼接[formData appendPartWithFormData:data name:[pair.field description]];转成AFHTTPBodyPart,添加到一个数组中
    3. block回去继续添加图片数据,中间有分隔符要注意下
    
    注意:post文件,content-type是application/x-www-form-urlencoded,formdata是UTF-8 data流数据, AFStreamingMultipartFormData实现分片上传,避免内存暴增
    普通post,content-type是applecation/json,formdata是json
    
    
    
    

    相关文章

      网友评论

          本文标题:AFNetworking详解

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