iOS源码解析—AFNetworking(RequestSeri

作者: egoCogito_panf | 来源:发表于2017-03-14 14:02 被阅读2926次

    概述

    ​ AFNetworking作为著名的网络请求框架,被开发者广泛运用。自从AF3.x开始,废弃了之前NSURLConnection的版本,使用NSURLSession,但是之前的AF2.x版本仍然有许多值得学习的地方。以2.6.2版本为例,本系列将对该框架进行全面的学习和分析。AFNetworking框架主要包含网络通信、序列化/反序列化、网络性能监听、网络通信安全四个模块,本文主要讲解AFURLRequestSerialization相关类。

    初始化

    AFHTTPRequestSerializer是用于构建NSURLRequest的类,通过-init:方法进行初始化,指定编码方式为NSUTF-8。HTTP请求分为请求头和请求体,请求头主要包含以下字段:

    User-Agent、Pragma、Content-Type、Content-Length、Accept-Language、Accept、Accept-Encoding、Cookie

    init方法,指定了请求头的Accept-Language,根据版本号、操作系统版本、设备屏幕scale等信息生成User-Agent,然后设置一些参数,如HTTPMethodsEncodingParametersInURI包含了指定的HTTP method,mutableObservedChangedKeyPaths存放监听到的属性,同时开始kvo监听。部分代码注释如下:

    - (instancetype)init {
        self.stringEncoding = NSUTF8StringEncoding; //UTF-8
        self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary]; //请求头配置
        ...
        [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"]; //设置Accept-Language字段
        ...
        [self setValue:userAgent forHTTPHeaderField:@"User-Agent"];//设置User-Agent字段
        ...
        self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil]; //包含指定的http method
    
        self.mutableObservedChangedKeyPaths = [NSMutableSet set]; //kvo属性集合
        for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
            if ([self respondsToSelector:NSSelectorFromString(keyPath)]) { //kvo监听部分属性
                [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
            }
        }
        return self;
    }
    

    AFHTTPRequestSerializer重写了属性的set方法,手动实现kvo通知,这样当调用这些set方法时,会监听到这些属性值的改变,存入mutableObservedChangedKeyPaths中,例如:

    - (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess {
        [self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
        _allowsCellularAccess = allowsCellularAccess;
        [self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
    }
    

    下面开始构建NSURLRequest,构建的类型主要有两种,普通类型和multipart类型。首先看一下构建普通类型的方法。

    普通类型

    通过-requestWithMethod:URLString:parameters:error方法构建NSURLRequest,下面是代码注释:

    - (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                     URLString:(NSString *)URLString
                                    parameters:(id)parameters
                                         error:(NSError *__autoreleasing *)error
    {
        NSParameterAssert(method);
        NSParameterAssert(URLString);
        NSURL *url = [NSURL URLWithString:URLString]; //构建URL
        NSParameterAssert(url);
        //根据url创建URLRequest对象
        NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
        mutableRequest.HTTPMethod = method; //http method
    
        //监听的发生变化的属性,设置为http的请求头
        for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
            if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
                [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
            }
        }
        //进一步处理NSURLRequest
        mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
        return mutableRequest;
    }
    

    首先创建NSURLRequest对象,然后调用-requestBySerializingRequest:withParameters:error:方法根据参数进一步设置NSURLRequest对象,下面是代码注释:

    - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                                   withParameters:(id)parameters
                                            error:(NSError *__autoreleasing *)error
    {
        ...
        //构建请求头参数
        [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
                query = self.queryStringSerialization(request, parameters, &serializationError);
                ...
            } else {
                switch (self.queryStringSerializationStyle) {
                    case AFHTTPRequestQueryStringDefaultStyle:
                        //按照一定规则构建query
                        query = AFQueryStringFromParameters(parameters);
                        break;
                }
            }
        }
        if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
            if (query) { //将query拼在url后面
                mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
            }
        } else {
            ...
            if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
                [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
            }
            //query设置为httpBody
            [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
        }
        return mutableRequest;
    }
    

    该方法首先设置http请求的head参数,然后将parameters转化为url的query,通过AFQueryStringFromParameters方法返回一个字符串,格式是:"key1=value1&key2=value2",代码注释如下:

    static NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
        NSMutableArray *mutablePairs = [NSMutableArray array];
        for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
            [mutablePairs addObject:[pair URLEncodedStringValue]]; //url encode,返回key=value格式的字符串
        }
        return [mutablePairs componentsJoinedByString:@"&"];
    }
    

    思路是首先根据AFQueryStringPairsFromDictionary方法的会根据parameters创建一个数组,无论parameters是否嵌套,创建后的数组中的元素在同一层级。数组中的元素是AFQueryStringPair对象,包装了key/value,然后调用URLEncodedStringValue方法将pair对象转化为key=value格式的字符串,且key和value都做了encode。最后将返回的字符传用"&"符号连接,生成query字符串。

    接着判断当前http请求的method是否包含在HTTPMethodsEncodingParametersInURI中,如果是GET请求,则包含在内,会将生成的将query拼在url后面,因为GET请求不设置http的request body。如果是POST请求,则将query字符串序列化成nsdata后,设置为http的request body。

    另外,有两个子类继承了AFHTTPRequestSerializer类,即AFJSONRequestSerializer和AFPropertyListRequestSerializer,都重写了父类的方法,下面看一下AFJSONRequestSerializer的方法:

    - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                                   withParameters:(id)parameters
                                            error:(NSError *__autoreleasing *)error
    {
        NSParameterAssert(request);
        //如果是"GET、HEAD、DELETE"方法,和父类一样
        if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
            return [super requestBySerializingRequest:request withParameters:parameters error:error];
        }
        NSMutableURLRequest *mutableRequest = [request mutableCopy];
        [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
            if (![request valueForHTTPHeaderField:field]) {
                //设置http请求头参数
                [mutableRequest setValue:value forHTTPHeaderField:field];
            }
        }];
        //parameters,设置http请求体
        if (parameters) {
            if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
                [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
            }
            [mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];
        }
        return mutableRequest;
    }
    

    分析该方法,当http的method是POST类型时,AFJSONRequestSerializer和父类的处理不同,该方法将parameters序列化为JSON数据,设置为http的请求体,且请求头的"Content-Type"参数设置为"application/json"。AFPropertyListRequestSerializer的方法如下:

    - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                                   withParameters:(id)parameters
                                            error:(NSError *__autoreleasing *)error
    {
        ...
        if (parameters) {
            if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
                [mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"];
            }
            [mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]];
        }
        return mutableRequest;
    }
    
    

    该方法逻辑类似,当http的method是POST类型时,调用NSPropertyListSerialization的方法将parameters序列化成NSData,赋值给http的请求体。

    multipart类型

    Multipart是HTTP协议为web表单新增的上传文件的协议,基于POST方法,数据放在http请求体,不同于普通post请求的key/value格式或者,json格式,multipart类型的请求体较长,且遵循一定的格式,下面是格式:

    --BoundaryAaB03x 
    content-disposition: form-data; name="name"
    //空行
    abcdef... 
    --BoundaryAaB03x 
    content-disposition: form-data; name=”pic”; filename=“content.txt” 
    Content-Type: text/plain
    //空行 
    ...contents of abc.txt...
    --BoundaryAaB03x 
    content-disposition: form-data; name=”pic”; filename=“content.txt” 
    Content-Type: text/plain
    //空行 
    ...contents of abc.txt...
    --BoundaryAaB03x--
    

    可以看出请求的数据可以分成若干个段落,每个段落由分隔符隔开,且顶部和中间的分隔符前面有"--",底部的分隔符前后都有"--",每个段落的格式固定如下:

    头部字段
    //空行
    数据实体
    

    下面来分析一下multipart类型的请求方法:

    - (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
                                                  URLString:(NSString *)URLString
                                                 parameters:(NSDictionary *)parameters
                                  constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                                                      error:(NSError *__autoreleasing *)error
    {
        NSParameterAssert(method);
        //不支持GET、HEAD类型
        NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);
        //创建NSURLRequest对象
        NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];
        //创建AFStreamingMultipartFormData对象
        __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 {
                    data = [[pair.value description] dataUsingEncoding:self.stringEncoding];
                }
    
                if (data) {
                    //formData对象追加参数
                    [formData appendPartWithFormData:data name:[pair.field description]];
                }
            }
        }
        //formData对象追加参数
        if (block) {
            block(formData);
        }
        //设置request header、request body
        return [formData requestByFinalizingMultipartFormData];
    }
    

    首先排除了GET和HEAD类型的请求,然后根据url创建NSURLRequest对象,然后创建创建AFStreamingMultipartFormData对象,该对象负责为NSURLRequest对象拼接请求体数据,下面主要分析AFStreamingMultipartFormData对象的结构和方法:

    @interface AFStreamingMultipartFormData ()
    @property (readwrite, nonatomic, copy) NSMutableURLRequest *request; //URLRequest
    @property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; //字符串编码
    @property (readwrite, nonatomic, copy) NSString *boundary; //边界标识
    @property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream;//body数据
    @end
     
    //初始化
    - (id)initWithURLRequest:(NSMutableURLRequest *)urlRequest
              stringEncoding:(NSStringEncoding)encoding
    {
        self = [super init];
        if (!self) {
            return nil;
        }
        self.request = urlRequest;
        self.stringEncoding = encoding;
        self.boundary = AFCreateMultipartFormBoundary(); //生成边界字符串
        self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding]; //创建请求体数据
        return self;
    }
    

    AFStreamingMultipartFormData内部维护了一个NSMutableURLRequest对象,编码方式(字符串转成NAData的编码方式),边界字符串和AFMultipartBodyStream对象,其中AFMultipartBodyStream对象负责维护请求体的各个数据段落。下面是该对象的定义注释:

    @interface AFMultipartBodyStream () <NSCopying>
    @property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
    @property (readwrite, nonatomic, strong) NSMutableArray *HTTPBodyParts;
    @property (readwrite, nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator;
    @property (readwrite, nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart;
    @property (readwrite, nonatomic, strong) NSOutputStream *outputStream;
    @property (readwrite, nonatomic, strong) NSMutableData *buffer;
    @end
    

    其中HTTPBodyParts是一个数组,数组中每个元素是一个AFHTTPBodyPart对象,AFHTTPBodyPart对象封装了请求体数据中每个段落的信息,代码注释如下:

    @interface AFHTTPBodyPart : NSObject
    @property (nonatomic, assign) NSStringEncoding stringEncoding; //编码方式
    @property (nonatomic, strong) NSDictionary *headers; //段落头部信息
    @property (nonatomic, copy) NSString *boundary; //边界字符串
    @property (nonatomic, strong) id body; //段落体数据
    @property (nonatomic, assign) unsigned long long bodyContentLength; //段落体数据长度
    @property (nonatomic, strong) NSInputStream *inputStream; //数据流,用于读取body内如进buffer
    @property (nonatomic, assign) BOOL hasInitialBoundary; //是否是开始边界
    @property (nonatomic, assign) BOOL hasFinalBoundary; //是否是结束边界
    ...
    @end
    

    -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:方法接下来将parameters参数转化成AFQueryStringPair对象数组,将每个pair对象的key和value添加到formData对象维护的段落数组中。formData支持三种格式的数据,即NData、FileURL和NSInputStream,下面依次分析一下:

    1. NSData格式:

      例如-appendPartWithFormData:name:方法和-appendPartWithFileData:name:fileName:mimeType:方法,首先拼装段落头部信息,设置Content-Disposition字段和Content-Type字段,然后将data作为body构建AFHTTPBodyPart对象,添加进bodyStream中的段落数组HTTPBodyParts中。下面是代码注释:

      - (void)appendPartWithFileData:(NSData *)data
                                name:(NSString *)name
                            fileName:(NSString *)fileName
                            mimeType:(NSString *)mimeType
      {
          ...
          NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
          [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
          [mutableHeaders setValue:mimeType forKey:@"Content-Type"];
          [self appendPartWithHeaders:mutableHeaders body:data]; //mutableHeaders和data包装成AFHTTPBodyPart对象,加入HTTPBodyParts数组中
      }
      
    2. FileURL格式:

      例如-appendPartWithFileURL:name:fileName:mimeType:error:方法,首先构建段落头部信息,设置Content-Disposition字段和Content-Type字段,然后构建AFHTTPBodyPart对象,body属性是fileURL,即本地文件路径,bodyContentLength属性是文件的大小,最后添加进HTTPBodyParts中。下面是代码注释:

      - (BOOL)appendPartWithFileURL:(NSURL *)fileURL
                               name:(NSString *)name
                           fileName:(NSString *)fileName
                           mimeType:(NSString *)mimeType
                              error:(NSError * __autoreleasing *)error
      {
          ...
          NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
          [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
          [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; //段落头部headers
          AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; //创建AFHTTPBodyPart对象
          bodyPart.stringEncoding = self.stringEncoding;
          bodyPart.headers = mutableHeaders;
          bodyPart.boundary = self.boundary;
          bodyPart.body = fileURL; //body属性是文件路径
          bodyPart.bodyContentLength = [fileAttributes[NSFileSize] unsignedLongLongValue];
          [self.bodyStream appendHTTPBodyPart:bodyPart]; //添加进HTTPBodyParts中
          return YES;
      }
      
    3. NSInputStream格式:

      例如-appendPartWithInputStream:name:fileName:length:mimeType:方法,首先构建段落信息,设置Content-Disposition字段和Content-Type字段,然后构建AFHTTPBodyPart对象,body属性是NSInputStream对象,添加进HTTPBodyParts中。下面是代码注释:

      - (void)appendPartWithInputStream:(NSInputStream *)inputStream
                                   name:(NSString *)name
                               fileName:(NSString *)fileName
                                 length:(int64_t)length
                               mimeType:(NSString *)mimeType
      {
          ...
          NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
          [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
          [mutableHeaders setValue:mimeType forKey:@"Content-Type"];//段落头部headers
          AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; //创建AFHTTPBodyPart对象
          bodyPart.stringEncoding = self.stringEncoding;
          bodyPart.headers = mutableHeaders;
          bodyPart.boundary = self.boundary;
          bodyPart.body = inputStream; //body属性是NSInputStream
          bodyPart.bodyContentLength = (unsigned long long)length;
          [self.bodyStream appendHTTPBodyPart:bodyPart]; //添加进HTTPBodyParts中
      }
      

    以上3种格式的数据添加进HTTPBodyParts中时,在接下来的过程中,都会转化二进制数据拼装在http的请求体报文中。

    -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:方法接下来调用block(formData)供外界调用层继续添加数据,方式可以使以上3种中的任意一种。然后调用-requestByFinalizingMultipartFormData方法为NSURLRequest对象添加请求报文的头,因为采用multipart类型的报文格式,所以设置Content-Type为"multipart/form-data",下面是代码注释:

    - (NSMutableURLRequest *)requestByFinalizingMultipartFormData {
        if ([self.bodyStream isEmpty]) {
            return self.request;
        }
        //设置出初始和结尾的边界
        [self.bodyStream setInitialAndFinalBoundaries];
        //将bodyStream作为请求报文体
        [self.request setHTTPBodyStream:self.bodyStream];
        //设置请求头的Content-Type和Content-Length字段
        [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"];
        [self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];
        return self.request;
    }
    

    其中self.bodyStream属性是AFMultipartBodyStream类型,继承于NSInputStream,将该属性设置为http的HTTPBodyStream属性,这样在发送http请求时,会按照文件流的方式发送请求报文,且上传数据是分片的,不会一次性将报文体数据读入内存中后发送,关于这种方式的比较,可以参考JSPatch大神bang的文章

    接下来通过NSURLSession发送请求时,调用-uploadTaskWithStreamedRequest:方法构建NSURLSessionUploadTask对象uploadTask,调用resume开始发送请求。由于已经设置bodyStream为NSInputStream对象,这种方式发送请求时会读取bodyStream的数据,调用NSInputStream对象的-read:maxLength:方法,而AFMultipartBodyStream对象重写了-read:maxLength:方法,代码注释如下:

    - (NSInteger)read:(uint8_t *)buffer
            maxLength:(NSUInteger)length
    {
        ...
        while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) {
            if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {
                if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {
                    break;
                }
            } else {
                NSUInteger maxLength = length - (NSUInteger)totalNumberOfBytesRead;
                NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&buffer[totalNumberOfBytesRead] maxLength:maxLength];
                if (numberOfBytesRead == -1) {
                    self.streamError = self.currentHTTPBodyPart.inputStream.streamError;
                    break;
                } else {
                    totalNumberOfBytesRead += numberOfBytesRead;
                    if (self.delay > 0.0f) {
                        [NSThread sleepForTimeInterval:self.delay];
                    }
                }
            }
        }
    #pragma clang diagnostic pop
        return totalNumberOfBytesRead;
    }
    

    该方法遍历HTTPBodyParts数组中的各个元素,其中HTTPBodyPartEnumerator是枚举器,依附于HTTPBodyParts数组,调用nextObject方法取出数组中的对象作为当前对象currentHTTPBodyPart。然后调用AFHTTPBodyPart的-read:maxLength:方法将AFHTTPBodyPart对象中的各个部分读入buffer中。下面是代码注释:

    - (NSInteger)read:(uint8_t *)buffer
            maxLength:(NSUInteger)length
    {
        NSInteger totalNumberOfBytesRead = 0;
        if (_phase == AFEncapsulationBoundaryPhase) { //边界部分
            ...
        }
        if (_phase == AFHeaderPhase) { //header部分
            ...
        }
        if (_phase == AFBodyPhase) { //body部分
            ...
        }
        if (_phase == AFFinalBoundaryPhase) { //尾部边界
            ...
        }
        return totalNumberOfBytesRead;
    }
    

    通过一组枚举类型AFHTTPBodyPartReadPhase表示段落中的不同部分,依次将它们生成报文数据,然后读入buffer中,生成数据的时候需要按照rfc1867规定,在相应地方添加\r\n换行符。当前阶段读完数据后调用-transitionToNextPhase方法进入下一个部分。需要注意的是,_phase==AFBodyPhase阶段时,通过self.inputStream对象读取数据进buffer中,self.inputStream是内部持有的一个属性,body中的数据由inputStream负责加载,通过懒加载的方式初始化:

    - (NSInputStream *)inputStream {
        if (!_inputStream) {
            if ([self.body isKindOfClass:[NSData class]]) {
                _inputStream = [NSInputStream inputStreamWithData:self.body];
            } else if ([self.body isKindOfClass:[NSURL class]]) {
                _inputStream = [NSInputStream inputStreamWithURL:self.body];
            } else if ([self.body isKindOfClass:[NSInputStream class]]) {
                _inputStream = self.body;
            } else {
                _inputStream = [NSInputStream inputStreamWithData:[NSData data]];
            }
        }
        return _inputStream;
    }
    

    inputStream会根据之前存入AFHTTPBodyPart对象中的body数据类型来初始化inputStream,说明之前3种格式的数据最终都会转化成NSInputStream对象inputStream,然后读取inputStream就可以。关于读取body数据进buffer的代码注释如下:

    if (_phase == AFBodyPhase) {
        NSInteger numberOfBytesRead = 0;
        //从inputStream中读取数据
        numberOfBytesRead = [self.inputStream read:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
        if (numberOfBytesRead == -1) {
            return -1;
        } else {
            totalNumberOfBytesRead += numberOfBytesRead;
            if ([self.inputStream streamStatus] >= NSStreamStatusAtEnd) {
                [self transitionToNextPhase];
            }
        }
    }
    

    totalNumberOfBytesRead表示总共读取的字节数。AFMultipartBodyStream对象通过重写父类的-read:maxLength:方法实现了拼接请求报文的body数据。

    相关文章

      网友评论

        本文标题:iOS源码解析—AFNetworking(RequestSeri

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