AFNetworking, as read in this article, is version 3.2.0.

The AFHTTPRequestSerializer class is used to build the NSMutableURLRequest. It serializes the request data using the HTTP request method (for example: GET) method, request URLURLString, and request parameters to instantiate NSMutableURLRequest object Request

1. Two global methods

1.1 Percent-code the incoming string

FOUNDATION_EXPORT NSString * AFPercentEscapedStringFromString(NSString *string);
Copy the code
Nsstrings * AFPercentEscapedStringFromString (nsstrings * string) {/ / in RFC3986 pointed out in section 3.4, in the query field percent encoding, reserved characters of "?" And "/" can not be coded, everything else needs to be coded. static NSString * const kAFCharactersGeneralDelimitersToEncode = @: # @ "[]"; // does not include "?" or "/"Due to the RFC 3986 - Section 3.4 static nsstrings * const kAFCharactersSubDelimitersToEncode = @! "" * + $& '(),; ="; // Get the allowed character of the URL query field and delete the "? "from it. And "/" reserved characters NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]]; // FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028 //return[string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; Static NSUInteger const batchSize = 50; static NSUInteger const batchSize = 50; NSUInteger index = 0; NSMutableString *escaped = @"".mutableCopy;

    while (index < string.length) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wgnu"
        NSUInteger length = MIN(string.length - index, batchSize);
#pragma GCC diagnostic popNSRange range = NSMakeRange(index, length); // Each Chinese or English length in NSString is 1, but an Emoji length is 2 or 4, // To avoid breaking up character sequences such as 👴🏻👮🏽 range = [string rangeOfComposedCharacterSequencesForRange:range]; NSString *substring = [string substringWithRange:range]; NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; [escaped appendString:encoded]; index += range.length; }return escaped;
}
Copy the code

1.2 Default encoding of incoming request parameters

FOUNDATION_EXPORT NSString * AFQueryStringFromParameters(NSDictionary *parameters);
Copy the code
Nsstrings * AFQueryStringFromParameters (NSDictionary * parameters) {/ / convert the incoming dictionary to array elements as AFQueryStringPair object, I then iterate over the number array and turn AFQueryStringPair into a percentage encoded NSString of type Key = Value, Finally, concatenate a string with "&" NSMutableArray *mutablePairs = [NSMutableArray array];for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
        [mutablePairs addObject:[pair URLEncodedStringValue]];
    }

    return [mutablePairs componentsJoinedByString:@"&"];
}
Copy the code

The above method calls the following method and passes parameters as an argument

NSArray * AFQueryStringPairsFromDictionary (NSDictionary * dictionary) {/ / the first parameter to the key pass nil, the second parameter value above the method to get the dictionaryreturn AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}
Copy the code

The following method takes the dictionary and turns it into an array of elements that are AFQueryStringPair

NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) { NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; // Set sort description to sort by the alphabetically ascending order of the description property of the object NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description"ascending:YES selector:@selector(compare:)]; // If value is passed in NSDictionaryif([value isKindOfClass:[NSDictionary class]]) {// Declare a variable to save the passed dictionary NSDictionary *dictionary = value; // Sort dictionary keys to ensure consistent orderingin query string, whichis important when deserializing potentially ambiguous sequences, Such as an array of dictionaries // Walk through the dictionary keys in ascending alphabetical orderfor (id nestedKey in[dictionary. AllKeys sortedArrayUsingDescriptors: @ [sortDescriptor]]) {/ / if the value of the traverse the key is not null, recursive calls to the method, If there is a key value, pass (key[nestedKey], nestedValue), otherwise pass (nestedKey, nestedValue) id nestedValue = dictionary[nestedKey];if (nestedValue) {
                [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"% @ [% @]." ", key, nestedKey] : nestedKey), nestedValue)]; }} // If value is passed NSArray}else if([value isKindOfClass:[NSArray class]]) {// Declare a variable to save the array passed NSArray *array = value; // go through the number groupfor (id nestedValue inArray) {// Call this method recursively, passing (key[], nestedValue) if there is a key, Otherwise the ((null) [], nestedValue) [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"% @ []", key], nestedValue)]; } // if value is passed in NSSet}else if([value isKindOfClass:[NSSet class]]) {// Declare a variable to hold the incoming collection NSSet *set= value; // Iterate over the elements of the collection in ascending alphabetical orderfor (id obj in [setSortedArrayUsingDescriptors: @ [sortDescriptor]]) {/ / recursive calls to this method, if there are key values (key, obj), Otherwise the ((null), obj) [mutableQueryStringComponents addObjectsFromArray: AFQueryStringPairsFromKeyAndValue (key, obj)]; } // If value is not passed as a collection object}else{/ / using the key and the value of the incoming parameters instantiation AFQueryStringPair object and added to the array mutableQueryStringComponents [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]]; } // Return an array of dictionary converted elements into AFQueryStringPair objectsreturn mutableQueryStringComponents;
}
Copy the code

2. Two agreements

2.1. AFURLRequestSerialization agreement

This protocol defines a method to concatenate the parameters number into an NSURLRequest object. Which class AFHTTPRequestSerializer, AFJSONRequestSerializer and AFPropertyListRequestSerializer all abide by this agreement

@protocol AFURLRequestSerialization <NSObject, NSSecureCoding, NSCopying>

- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(nullable id)parameters
                                        error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;

@end
Copy the code

2.2. AFMultipartFormData agreement

This protocol defines a series of methods are used to in – multipartFormRequestWithMethod: parameters: constructingBodyWithBlock: error: method of code block for the formData add data

*/ - (BOOL)appendPartWithFileURL:(NSURL *)fileURL name:(NSString *)name error:(NSError * _Nullable __autoreleasing *)error; /** Add the specified path to the form. */ - (BOOL)appendPartWithFileURL:(NSURL *)fileURL name:(NSString *)name fileName:(NSString *)fileName mimeType:(NSString *)mimeType error:(NSError * _Nullable __autoreleasing *)error; / * * will specify the input stream of data to be added to the form of * / - (void) appendPartWithInputStream: (nullable inputStream NSInputStream *) name: (nsstrings *) name fileName:(NSString *)fileName length:(int64_t)length mimeType:(NSString *)mimeType; /** Add the specified NSData object to the form, */ - (void)appendPartWithFileData:(NSData *)data name:(NSString *)name fileName:(NSString *)fileName mimeType:(NSString *)mimeType; /** add the specified NSData object to the form */ - (void)appendPartWithFormData (NSData *)data name (NSString *)name; */ - (void)appendPartWithHeaders:(NSDictionary <NSString *, NSString *> *)headers body:(NSData *)body; / * * by setting the request of bandwidth and delay time to raise the successful rate of to upload data in weak network environment * / - (void) throttleBandwidthWithPacketSize numberOfBytes: (NSUInteger) delay:(NSTimeInterval)delay;Copy the code

3. an enumeration

Next, you see an enumeration that defines the encoding of the requested query field, although only one default is defined for now

typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) {
    AFHTTPRequestQueryStringDefaultStyle = 0,
};
Copy the code

4. Three classes

Can. H file to see there are three classes, respectively is AFHTTPRequestSerializer and its two subclasses AFJSONRequestSerializer, AFPropertyListRequestSerializer, Take a look at the AFHTTPRequestSerializer class

4.1 AFHTTPRequestSerializer class

Take a look at the exposed interface in the.h file

4.1.1 Interface

4.1.1.1 properties

/** stringEncoding, default is NSUTF8StringEncoding */ @property (nonatomic, assign) NSStringEncoding stringEncoding; */ @property (nonatomic, assign) BOOL allowsCellularAccess; / / @property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy; */ @property (nonatomic, assign) BOOL HTTPShouldHandleCookies; /** Whether to use pipeting, that is, whether to wait until the response to the previous request is received before sending a later request, pipeting can send a group of requests one at a time without waiting, Default: No */ @property (nonatomic, assign) BOOL HTTPShouldUsePipelining; / * * network service type, the system will automatically according to the type of set optimization * / @ property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType; /** Timeout duration, default is 60 seconds */ @property (nonatomic, assign) NSTimeInterval timeoutInterval; /** request header */ @property (readonly, nonatomic, strong) NSDictionary <NSString *, NSString *> *HTTPRequestHeaders;
Copy the code

4.1.1.2 method

/** Instantiate the default object method */ + (instanceType)serializer; /** Set the field and value of the request header, remove the field if the value is nil */ - (void)setValue:(nullable NSString *)value
forHTTPHeaderField:(NSString *)field; */ - (NSString *)valueForHTTPHeaderField:(NSString *)field; /** Assign the Authorization field of the request header with the account and password */ - (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username password:(NSString *)password; /** Clear the value of the Authorization field from the request header */ - (void)clearAuthorizationHeader; /** To concatenate the query string encoding into the collection of HTTP request methods following the URL, Defaults to the GET, HEAD, and DELETE * / @ property (nonatomic, strong) NSSet < > nsstrings * * HTTPMethodsEncodingParametersInURI; /** Set the encoding method of the query string. Currently, AFNetworking only implements one encoding method, namely percentage encoding */ - (void).setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style; /** Set custom query string encoding method, only need to implement encoding in block */ - (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block; /** Generates an NSMutableURLRequest object using the passed HTTP request method, request URL, and request parameters. When the HTTP request method is GET, HEAD, or DELETE, the parameters are concatenated to the URL; otherwise, */ - (NSMutableURLRequest *)requestWithMethod (NSString *)method URLString (NSString *)URLString parameters:(nullable id)parameters error:(NSError * _Nullable __autoreleasing *)error; /** Generates an NSMutableURLRequest object for a multipart/form-dat request using the passed HTTP request method, request URL, and request parameters. */ - (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(nullable NSDictionary <NSString *, id> *)parameters constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block error:(NSError * _Nullable __autoreleasing *)error; /** Remove the HTTPBodyStream from the original request and write it asynchronously to the specified path. Object and returns a NSMutableURLRequest * / - (NSMutableURLRequest *) requestWithMultipartFormRequest (NSURLRequest *) request writingStreamContentsToFile:(NSURL *)fileURL completionHandler:(nullable void (^)(NSError * _Nullable error))handler; @endCopy the code

After looking at the interface section, go back to the.m file and look at the private implementation

4.1.2 Private implementation

4.1.2.1 Private Global Methods

static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
    static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), 
                                                     NSStringFromSelector(@selector(cachePolicy)), 
                                                     NSStringFromSelector(@selector(HTTPShouldHandleCookies)), 
                                                     NSStringFromSelector(@selector(HTTPShouldUsePipelining)), 
                                                     NSStringFromSelector(@selector(networkServiceType)), 
                                                     NSStringFromSelector(@selector(timeoutInterval))];
    });

    return _AFHTTPRequestSerializerObservedKeyPaths;
}
Copy the code

The method takes the properties of the AFHTTPRequestSerializer object to observe through a singleton and returns them in an array.

4.1.2.2 Private Global Static Variables

static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerObserverContext;
Copy the code

This variable is used to identify the observer

4.1.2.3 class extensions

@interface AFHTTPRequestSerializer () // The property of the user-defined AFHTTPRequestSerializer object that needs to be observed. strong) NSMutableSet *mutableObservedChangedKeyPaths; / / used to store the request header @ property (readwrite nonatomic, strong) NSMutableDictionary * mutableHTTPRequestHeaders; @property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle; @property (readWrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization; @endCopy the code

4.1.2.4 implementation

4.1.2.4.1 Life-cycle related methods
+ (instanceType)serializer {// Is the normal instantiation methodreturn [[self alloc] init];
}

- (instancetype)init {
    self = [super init];
    if(! self) {returnnil; } // initialize stringEncoding as NSUTF8StringEncoding self.stringEncoding = NSUTF8StringEncoding; / / initialize request header self. MutableHTTPRequestHeaders = [NSMutableDictionary dictionary]; / / retrieve the first five user preferences and assigned to the request header Accept - Language field NSMutableArray * acceptLanguagesComponents = [NSMutableArray array]; [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {floatQ = 1.0f - (idx * 0.1f); [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"% @. Q = % 0.1 g", obj, q]];
        *stop = q <= 0.5f;
    }];
    [self setValue:[acceptLanguagesComponents componentsJoinedByString:@","] forHTTPHeaderField:@"Accept-Language"]; // Get the project name (if not, get the BundleID), application Version Version (if not, get the application Build Version), device type, system Version, and screen scaling ratio and assign values to the user-Agent field NSString *userAgent = in the request header 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.2 f)", [[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.2 f)", [[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 lossless ASCII encoding is not possible, that is, not just plain characters or ASCII codesif(! [userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) { NSMutableString *mutableUserAgent = [userAgent mutableCopy]; // If you remove all characters that are not in the ASCII range, assign again after removalif (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: / / http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html / / need to initialize the query string coding splicing set behind the URL of the HTTP request method to GET, HEAD, and DELETE methods self.HTTPMethodsEncodingParametersInURI = [NSSetsetWithObjects:@"GET"The @"HEAD"The @"DELETE", nil]; / / initialize the custom AFHTTPRequestSerializer attribute set to observe the self. MutableObservedChangedKeyPaths = [NSMutableSetset]; / / traverse AFHTTPRequestSerializer need to add the observation of the properties of the added observer, and set the context for AFHTTPRequestSerializerObserverContext identifiesfor (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
            [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext]; }}returnself; } - (void)dealloc {// Navigate through AFHTTPRequestSerializer to add observation attributes and remove observersfor (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
            [self removeObserver:self forKeyPath:keyPath context:AFHTTPRequestSerializerObserverContext]; }}}Copy the code
4.1.2.4.2 Manual IMPLEMENTATION of KVO
- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess {
    [self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
    _allowsCellularAccess = allowsCellularAccess;
    [self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
}

- (void)setCachePolicy:(NSURLRequestCachePolicy)cachePolicy {
    [self willChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))];
    _cachePolicy = cachePolicy;
    [self didChangeValueForKey:NSStringFromSelector(@selector(cachePolicy))];
}

- (void)setHTTPShouldHandleCookies:(BOOL)HTTPShouldHandleCookies {
    [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))];
    _HTTPShouldHandleCookies = HTTPShouldHandleCookies;
    [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldHandleCookies))];
}

- (void)setHTTPShouldUsePipelining:(BOOL)HTTPShouldUsePipelining {
    [self willChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldUsePipelining))];
    _HTTPShouldUsePipelining = HTTPShouldUsePipelining;
    [self didChangeValueForKey:NSStringFromSelector(@selector(HTTPShouldUsePipelining))];
}

- (void)setNetworkServiceType:(NSURLRequestNetworkServiceType)networkServiceType {
    [self willChangeValueForKey:NSStringFromSelector(@selector(networkServiceType))];
    _networkServiceType = networkServiceType;
    [self didChangeValueForKey:NSStringFromSelector(@selector(networkServiceType))];
}

- (void)setTimeoutInterval:(NSTimeInterval)timeoutInterval {
    [self willChangeValueForKey:NSStringFromSelector(@selector(timeoutInterval))];
    _timeoutInterval = timeoutInterval;
    [self didChangeValueForKey:NSStringFromSelector(@selector(timeoutInterval))];
}
Copy the code
4.1.2.4.3 Customize setters and getters for public properties
- (NSDictionary *) HTTPRequestHeaders {/ / mutableHTTPRequestHeaders private property is returnedreturn [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
}

- (void)setValue:(NSString *)value
forHTTPHeaderField: (field nsstrings *) {/ / for private property mutableHTTPRequestHeaders assignment [self. MutableHTTPRequestHeaderssetValue:value forKey:field]; } - (nsstrings *) valueForHTTPHeaderField: (nsstrings *) field {/ / access to private property mutableHTTPRequestHeaders specified key valuereturn [self.mutableHTTPRequestHeaders valueForKey:field];
}

- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username password:(NSString *)password { // Concatenate the account and password into a string and convert it to an NSData object in UTF8 format. The Authorization field NSData *basicAuthCredentials = [[NSString stringWithFormat:@ is assigned to the request header through base64 encoded as a string"% @ : % @", username, password] dataUsingEncoding:NSUTF8StringEncoding];
    NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0];
    [self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] forHTTPHeaderField:@"Authorization"]; } - (void) clearAuthorizationHeader {/ / removed from the request header Authorization field [self. MutableHTTPRequestHeaders removeObjectForKey: @"Authorization"];
}

- (void)setQueryStringSerializationWithStyle style: (AFHTTPRequestQueryStringSerializationStyle) {/ / if set the encoding format is put custom coding block nil self.queryStringSerializationStyle = style; self.queryStringSerialization = nil; } - (void)setQueryStringSerializationWithBlock:(NSString *(^)(NSURLRequest *, id, __autoreleasing NSError * *)) block {/ / this is for the user to set the code block there are smart tips, you can directly enter on the self. The queryStringSerialization = block; }Copy the code
4.1.2.4.4 Implementation of public methods
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(id)parameters Error :(NSError *__autoreleasing *)error {// crash NSParameterAssert(method); NSParameterAssert(URLString); NSURL *url = [NSURL URLWithString:URLString]; // Determine whether the URL generates NSParameterAssert(url); // Use the generated NSURL object to generate the NSMutableURLRequest object, NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url]; mutableRequest.HTTPMethod = method; // Navigate through AFHTTPRequestSerializer to add the observed propertiesfor (NSString *keyPath inAFHTTPRequestSerializerObservedKeyPaths ()) {/ / if the traversal is custom attributeif([self mutableObservedChangedKeyPaths containsObject: keyPath]) {/ / attribute assigned to the corresponding value NSMutableURLRequest object corresponding to the attribute [mutableRequestsetValue:[self valueForKeyPath:keyPath] forKey:keyPath]; }} / / will be treated as the parameters of the incoming parameters after added to the mutableRequest mutableRequest = [[self requestBySerializingRequest: mutableRequest withParameters:parameters error:error] mutableCopy]; / / return mutableRequestreturnmutableRequest; } - (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block Error :(NSError *__autoreleasing *)error {// crash NSParameterAssert(method); // The request method is GET or HEAD on crash NSParameterAssert(! [method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]); NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error]; / / AFStreamingMultipartFormData formData object generated by NSMutableURLRequest object __block AFStreamingMultipartFormData * formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding]; // If an argument is passedif(parameters) {// Convert passed dictionary parameters to an array of elements that are AFQueryStringPair objects and iterate overfor (AFQueryStringPair *pair inAFQueryStringPairsFromDictionary (parameters)) {/ / the object pair the value attribute to NSData object, and spell the formData object 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 appendPartWithFormData:data name:[pair.field description]]; }}} // Call the code block to concatenate the data you want to uploadif(block) { block(formData); } // Build a header unique to the multipart/form-data requestreturn[formData requestByFinalizingMultipartFormData]; } - (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request writingStreamContentsToFile:(NSURL *)fileURL completionHandler:(void (^)(NSError *error))handler {// if the request object's HTTPBodyStream property is nil then crash NSParameterAssert(request.HTTPBodyStream); // If fileURL is not a valid file path, crash NSParameterAssert([fileURL isFileURL]); NSInputStream *inputStream = request.HTTPBodyStream; NSOutputStream *outputStream = [[NSOutputStream alloc] initWithURL:fileURL append:NO]; __block NSError *error = nil; Dispatch_async (dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) ^{// Add input/output streams to the current running loop in default mode [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]forMode:NSDefaultRunLoopMode];
        [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; // Open input/output stream [inputStream open]; [outputStream open]; // If the input/output stream still has operable byteswhile([inputStream hasBytesAvailable] && [outputStream hasSpaceAvailable]) { uint8_t buffer[1024]; NSInteger bytesRead = [inputStream); // a maximum of 1024bytes of data are read from the inputStream at a time and stored in bufferread:buffer maxLength:1024];
            if (inputStream.streamError || bytesRead < 0) {
                error = inputStream.streamError;
                break; } NSInteger bytesWritten = [outputStream write:buffer maxLength:(NSUInteger)bytesRead];if (outputStream.streamError || bytesWritten < 0) {
                error = outputStream.streamError;
                break; } // If the reading and writing are done, the loop is brokenif (bytesRead == 0 && bytesWritten == 0) {
                break; }} // close input/outputStream [outputStream close]; [inputStream close]; Async callback in main queue if a callback block is passed inif(handler) { dispatch_async(dispatch_get_main_queue(), ^{ handler(error); }); }}); NSMutableURLRequest *mutableRequest = [Request mutableCopy]; // Set the HTTPBodyStream property of the mutableRequest object to nil. mutableRequest.HTTPBodyStream = nil;return mutableRequest;
}
Copy the code
The implementation of the 4.1.2.4.5 AFURLRequestSerialization agreement method
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error {// NSParameterAssert(request); NSMutableURLRequest *mutableRequest = [request mutableCopy]; // Iterate over the request header, Fields are of no value assignment [self. HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock: ^ (id field, id value, BOOL * __unused stop) {if(! [request valueForHTTPHeaderField:field]) { [mutableRequestsetValue:value forHTTPHeaderField:field]; }}]; NSString *query = nil;if// If a block of code is customized, it is encoded in a customized wayif (self.queryStringSerialization) {
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError);

            if (serializationError) {
                if (error) {
                    *error = serializationError;
                }

                returnnil; } // If the user does not customize the code block, use the AFNetworking default encoding method, i.e., percent encoding}else {
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                    query = AFQueryStringFromParameters(parameters);
                    break; }}} // If the HTTP request method is GET, HEAD, or DELETEif([self HTTPMethodsEncodingParametersInURI containsObject: [[request HTTPMethod] uppercaseString]]) {/ / the query string concatenation behind the urlif (query && query.length > 0) {
            mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"& % @" : @"? % @", query]]; } // If the HTTP request method is POST or PUT,}else{// concatenate the query string into the request body //#2864: an empty string is a valid x-www-form-urlencoded payload
        if(! query) { query = @"";
        }
        if(! [mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
        }
        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]]; } // Return the mutableRequest object with the concatenated parametersreturn mutableRequest;
}
Copy the code
4.1.2.4.6 KVO method callback processing
+ (BOOL) automaticallyNotifiesObserversForKey: (nsstrings *) key {/ / attributes of the object if it is need to observe AFHTTPRequestSerializer, does not automatically KVOif ([AFHTTPRequestSerializerObservedKeyPaths() containsObject:key]) {
        return NO;
    }

    return[super automaticallyNotifiesObserversForKey:key]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(__unused id)object change:(NSDictionary *)change Context :(void *)context {// if the AFHTTPRequestSerializer class is observed, add the observed attributesif(the context = = AFHTTPRequestSerializerObserverContext) {/ / if the value assigned to the current property does not null is added to the self. The mutableObservedChangedKeyPaths, Otherwise remove from itif ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
            [self.mutableObservedChangedKeyPaths removeObject:keyPath];
        } else{ [self.mutableObservedChangedKeyPaths addObject:keyPath]; }}}Copy the code
4.1.2.4.7 Implementation of NSSecureCoding protocol method

In iOS6, apple introduced a new protocol based on ns scoding called ns secure recoding. NSSecureCoding is the same as NSCoding, except that both the key and the class of the object to be decoded are specified when decoding. If the requested class does not match the class of the object decoded from the file, NSCoder will throw an exception telling you that the data has been tampered with.

+ (BOOL)supportsSecureCoding {// If a class complies with the NSSecureCoding protocol and returns YES on + supportsSecureCoding, it declares that it can handle the encoding and decoding of its own instance, To prevent replacement attacks.return YES;
}

- (instancetype)initWithCoder:(NSCoder *)decoder {
    self = [self init];
    if(! self) {return nil;
    }

    self.mutableHTTPRequestHeaders = [[decoder decodeObjectOfClass:[NSDictionary class] forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))] mutableCopy]; self.queryStringSerializationStyle = (AFHTTPRequestQueryStringSerializationStyle)[[decoder decodeObjectOfClass:[NSNumber  class]forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))] unsignedIntegerValue];

    return self;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.mutableHTTPRequestHeaders forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))];
    [coder encodeInteger:self.queryStringSerializationStyle forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))];
}
Copy the code
4.1.2.4.8 Implementation of NSCopying protocol method
- (instancetype)copyWithZone:(NSZone *)zone {
    AFHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init];
    serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone];
    serializer.queryStringSerializationStyle = self.queryStringSerializationStyle;
    serializer.queryStringSerialization = self.queryStringSerialization;

    return serializer;
}

@end
Copy the code

4.2 AFJSONRequestSerializer class

AFJSONRequestSerializer is a subclass of AFHTTPRequestSerializer, which can be used when the server requires that the data type to be uploaded be JSON

4.2.1 Interface

4.2.1.1 properties

@property (nonatomic, assign) NSJSONWritingOptions writingOptions;Copy the code

4.2.1.2 method

/ / instance chemical method + (instancetype) serializerWithWritingOptions (NSJSONWritingOptions) writingOptions;Copy the code

4.2.2 Private implementation parts

4.2.2.1 Implementation of public methods

+ (instanceType)serializer {// Call the following method and pass the default JSON output formatreturn[self serializerWithWritingOptions:(NSJSONWritingOptions)0]; } + (instancetype) serializerWithWritingOptions: NSJSONWritingOptions writingOptions {/ / call the parent class initialization method and save the incoming parameters AFJSONRequestSerializer *serializer = [[self alloc] init]; serializer.writingOptions = writingOptions;return serializer;
}
Copy the code

4.2.2.2 AFURLRequestSerialization agreement method implementation

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error {// NSParameterAssert(request); // If the HTTP request method is GET, HEAD, or DELETEif([self HTTPMethodsEncodingParametersInURI containsObject: [[request HTTPMethod] uppercaseString]]) {/ / call the superclass implementation directly and returnreturn[super requestBySerializingRequest:request withParameters:parameters error:error]; } NSMutableURLRequest *mutableRequest = [request mutableCopy]; // Iterate over the request header, Fields are of no value assignment [self. HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock: ^ (id field, id value, BOOL * __unused stop) {if(! [request valueForHTTPHeaderField:field]) { [mutableRequestsetValue:value forHTTPHeaderField:field]; }}]; // If an argument is passed inif(parameters) {// If the content-Type field in the mutableRequest request header has no valueif(! [mutableRequest valueForHTTPHeaderField:@"Content-Type"] {// Set the content-type field of the mutableRequest request header to Application /json [mutableRequest]setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; } // Convert the parameters passed in to a JSON-formatted NSData object and add it to the request body of mutableRequestsetHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];
    }

    return mutableRequest;
}
Copy the code

4.2.2.3 Implementation of NSSecureCoding protocol method

Add writingOptions to the parent class

- (instancetype)initWithCoder:(NSCoder *)decoder {
    self = [super initWithCoder:decoder];
    if(! self) {return nil;
    }

    self.writingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(writingOptions))] unsignedIntegerValue];

    return self;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    [super encodeWithCoder:coder];

    [coder encodeInteger:self.writingOptions forKey:NSStringFromSelector(@selector(writingOptions))];
}
Copy the code

4.2.2.4 Implementation of NSCopying protocol method

Also add the writingOptions property to the parent class

- (instancetype)copyWithZone:(NSZone *)zone {
    AFJSONRequestSerializer *serializer = [super copyWithZone:zone];
    serializer.writingOptions = self.writingOptions;

    return serializer;
}
Copy the code

4.3 AFPropertyListRequestSerializer class

AFPropertyListRequestSerializer is a subclass of AFHTTPRequestSerializer, such can pass parameters of the code into plist format NSDate object to the server, is generally used to transmit the data in XML format

4.3.1 Interface

4.3.1.1 properties

// plist output format @property (nonatomic, assign) NSPropertyListFormat format; / / plist coding type, and the present value didn't @ property (nonatomic, assign) NSPropertyListWriteOptions writeOptions;Copy the code

4.3.1.2 method

+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format writeOptions:(NSPropertyListWriteOptions)writeOptions; tions;Copy the code

4.3.2 Private implementation parts

4.3.2.1 Implementation of public methods

+ (instanceType)serializer {// Call the following instantiation method to set the output format of plist to XMLreturn[self serializerWithFormat:NSPropertyListXMLFormat_v1_0 writeOptions:0]; } + (instancetype)serializerWithFormat:(NSPropertyListFormat)format WriteOptions: NSPropertyListWriteOptions writeOptions {/ / call the parent class initialization method and save the AFPropertyListRequestSerializer incoming parameters *serializer = [[self alloc] init]; serializer.format = format; serializer.writeOptions = writeOptions;return serializer;
}
Copy the code

The implementation of the 4.3.2.2 AFURLRequestSerialization agreement method

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error {// NSParameterAssert(request); // If the HTTP request method is GET, HEAD, or DELETEif([self HTTPMethodsEncodingParametersInURI containsObject: [[request HTTPMethod] uppercaseString]]) {/ / call the superclass implementation directly and returnreturn[super requestBySerializingRequest:request withParameters:parameters error:error]; } NSMutableURLRequest *mutableRequest = [request mutableCopy]; // Iterate over the request header, Fields are of no value assignment [self. HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock: ^ (id field, id value, BOOL * __unused stop) {if(! [request valueForHTTPHeaderField:field]) { [mutableRequestsetValue:value forHTTPHeaderField:field]; }}]; // If an argument is passed inif(parameters) {// If the content-Type field in the mutableRequest request header has no valueif(! [mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {// // assign application/x-plist [mutableRequest] to the content-type field in the mutableRequest headersetValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"]; } // Convert the parameters passed into a PList-formatted NSData object and add it to the request body of mutableRequestsetHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]];
    }

    return mutableRequest;
}

Copy the code

5. Four global static constants

/** AFURLRequestSerializer class error, Error code corresponding NSURLErrorDomain error code * / FOUNDATION_EXPORT nsstrings * const AFURLRequestSerializationErrorDomain; / * * this key is only AFURLRequestSerializationErrorDomain, The corresponding value is NSURLRequest error request operation * / FOUNDATION_EXPORT nsstrings * const AFNetworkingOperationFailingURLRequestErrorKey; /** Maximum packet size in bytes of the throttling bandwidth of the HTTP request input stream. Is equal to 16 KB. */ FOUNDATION_EXPORT NSUInteger const kAFUploadStream3GSuggestedPacketSize; /** Throttling bandwidth of the HTTP request input stream The delay each time a packet is read. That equals 0.2 seconds. */ FOUNDATION_EXPORT NSTimeInterval const kAFUploadStream3GSuggestedDelay;Copy the code

6. Four private classes

6.1 AFQueryStringPair class

When encoding the query parameters of the request, the incoming parameters are split into the smallest collection object, which is then converted into an AFQueryStringPair object, and the field and value percentage signs are encoded to generate a string of type Field =value

6.1.1 Interface

6.1.1.1 properties

@property (readwrite, nonatomic, strong) id field; @property (readWrite, nonatomic, strong) id value; / / valueCopy the code

6.1.1.2 method

/** AFQueryStringPair initializes @param field @param value @paramreturnInitialize AFQueryStringPair */ - (instancetype)initWithField:(id)field value:(id)value; /** Concatenate the attributes field and value with a percentage sign to form a string @ with "="returnProcessed string */ - (NSString *)URLEncodedStringValue;Copy the code

6.1.2 Implementation

- (instancetype)initWithField:(id)field value:(id)value {
    self = [super init];
    if(! self) {returnnil; } self.field = field; self.value = value;returnself; } - (NSString *)URLEncodedStringValue {// If value is nil or nullif(! Self. Value | | [self value isEqual: [NSNull null]]) {/ / only describe the attribute field string attributes in percent encoded to returnreturnAFPercentEscapedStringFromString([self.field description]); // If value is not nil or null}else{// Concatenate the attributes field and value with a percentage sign into a string with "="return [NSString stringWithFormat:@"% @ = % @", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])]; }}Copy the code

6.2 AFHTTPBodyPart class

Each AFHTTPBodyPart represents a piece of form data, that is, data from a file to be uploaded, and reads its own internal data

6.2.1 Private global Properties

Static NSString * const kAFMultipartFormCRLF = @"\r\n"; / * * 3 g environment to upload suggested bandwidth * / NSUInteger const kAFUploadStream3GSuggestedPacketSize = 1024 * 16; / * * 3 g environment to upload suggested delay * / NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;Copy the code

6.2.2 Private global methods

/** Boundary string made up of randomly generated octet hexadecimal strings */ static NSString *AFCreateMultipartFormBoundary() {
    return [NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()]; } / * * generates start boundary string * / static inline nsstrings * AFMultipartFormInitialBoundary (nsstrings * boundary) {return [NSString stringWithFormat:@"-- % @ % @", boundary, kAFMultipartFormCRLF]; } / * * generate intermediate boundary string * / static inline nsstrings * AFMultipartFormEncapsulationBoundary (nsstrings * boundary) {return [NSString stringWithFormat:@"% @, % @ % @", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF]; } / * * generates end boundary string * / static inline nsstrings * AFMultipartFormFinalBoundary (nsstrings * boundary) {return [NSString stringWithFormat:@"% @, % @, % @", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF]; } /** Get the MIME type of the file based on the file suffix, Namely that the value of the content-type * / static inline nsstrings * AFContentTypeForPathExtension (nsstrings * extension) {/ / Generates a UTI string from the file suffix string passed in. (A uniform type identifier is a string that uniquely identifies an abstract type. They can be used to describe file formats or in-memory data types, but they can also be used to describe other types of entity types, such as directories, volumes, or packages. NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL); / / to UTI to MIME type nsstrings * contentType = (__bridge_transfer nsstrings *) UTTypeCopyPreferredTagWithClass ((__bridge CFStringRef)UTI, kUTTagClassMIMEType);if(! contentType) {return @"application/octet-stream";
    } else {
        returncontentType; }}Copy the code

6.2.3 Interface

6.2.3.1 properties

/** Code */ @property (nonatomic, assign) NSStringEncoding stringEncoding; / / @property (nonatomic, strong) NSDictionary *headers; /** boundary */ @property (nonatomic, copy) NSString *boundary; /** content */ @property (nonatomic, strong) id body; /** ContentLength */ @property (nonatomic, assign) unsigned long Long bodyContentLength; / / @property (nonatomic, strong) NSInputStream *inputStream; / / @property (nonatomic, assign) BOOL hasInitialBoundary; /** If there is an end boundary */ @property (nonatomic, assign) BOOL hasFinalBoundary; /** content length */ @property (readonly, nonatomic, assign, getter = hasBytesAvailable) BOOL bytesAvailable; /** content length */ @property (readonly, nonatomic, assign) unsigned long long contentLength;
Copy the code

6.2.3.2 method

/** The AFHTTPBodyPart object reads out its own data and writes it to the buffer passed in */ - (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)length;
Copy the code

6.2.4 Private Enumeration

Typedef enum {AFEncapsulationBoundaryPhase = 1, / / boundary between paragraphs AFHeaderPhase = 2, / / the first paragraph AFBodyPhase = 3, AFFinalBoundaryPhase = 4,} AFHTTPBodyPartReadPhase;Copy the code

6.2.5 Class Extensions

6.2.5.1 Member Variables

*/ AFHTTPBodyPartReadPhase _phase; */ AFHTTPBodyPartReadPhase _phase; /** Save the input stream generated by the body property of the AFHTTPBodyPart object */ NSInputStream *_inputStream; */ unsigned long long _phaseReadOffset; */ unsigned long long _phaseReadOffset;Copy the code

6.2.5.2 Private method declarations

/** Switches to the next paragraph for reading, i.e. controls the state machine */ - (BOOL)transitionToNextPhase; /** Write the NSDdata object stored in the attributes of the AFHTTPBodyPart object to buffer */ - (NSInteger)readData:(NSData *)data
           intoBuffer:(uint8_t *)buffer
            maxLength:(NSUInteger)length;
Copy the code

6.2.6 Implementation

- (instancetype)init {
    self = [super init];
    if(! self) {returnnil; } / / switch to the main thread, initialization of member variables _phase AFEncapsulationBoundaryPhase, _phaseReadOffset 0 [self transitionToNextPhase];returnself; } - (void)dealloc {// Close the input stream and empty itif(_inputStream) { [_inputStream close]; _inputStream = nil; }} /** inputStream {- (NSInputStream)inputStream {if(! _inputStream) {// Generate the corresponding NSInputStream object based on the class of the body property and save itif ([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; } /** Concatenates the dictionary type data saved by the headers attribute into a string of the specified format */ - (NSString *)stringForHeaders {NSMutableString *headerString = [NSMutableString string];for (NSString *field in [self.headers allKeys]) {
        [headerString appendString:[NSString stringWithFormat:@"% @ : % @ % @", field, [self.headers valueForKey:field], kAFMultipartFormCRLF]];
    }
    [headerString appendString:kAFMultipartFormCRLF];

    return[NSString stringWithString:headerString]; */ - (unsigned long long)contentLength {unsigned long long length = 0; // If there is a start boundary, the start boundary string is generated, otherwise the middle boundary string is generated, and then the corresponding NSData object is generated, And get the length NSData * encapsulationBoundaryData = [([self hasInitialBoundary]? AFMultipartFormInitialBoundary (self. A boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; length += [encapsulationBoundaryData length]; The length of the corresponding NSData object / / add the header NSData * headersData = [[self stringForHeaders] dataUsingEncoding: self. StringEncoding]; length += [headersData length]; // Add body to NSData object length += _bodyContentLength; // Generate an end boundary string if there is an end boundary, otherwise generate an intermediate boundary string, then generate the corresponding NSData object and add NSData *closingBoundaryData = ([self hasFinalBoundary]? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); length += [closingBoundaryData length];returnlength; } /** Determines whether there is readable data */ - (BOOL)hasBytesAvailable {// Allows'read:maxLength:` to be called again if `AFMultipartFormFinalBoundary` does not fit into the available buffer
    if (_phase == AFFinalBoundaryPhase) {
        returnYES; } // Check whether there is readable data according to the streamStatus property of inputStream#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcovered-switch-default"
    switch (self.inputStream.streamStatus) {
        case NSStreamStatusNotOpen:
        case NSStreamStatusOpening:
        case NSStreamStatusOpen:
        case NSStreamStatusReading:
        case NSStreamStatusWriting:
            return YES;
        case NSStreamStatusAtEnd:
        case NSStreamStatusClosed:
        case NSStreamStatusError:
        default:
            return NO;
    }
#pragma clang diagnostic pop} /** Write its own data to buffer */ - (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)length { NSInteger totalNumberOfBytesRead = 0; // If the paragraph to be read is a middle boundary paragraphif(_phase = = AFEncapsulationBoundaryPhase) {/ / according to whether have started boundary to generate the corresponding boundary string, and then generate the corresponding NSData object, Written into the butter NSData * encapsulationBoundaryData = [([self hasInitialBoundary]? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding]; totalNumberOfBytesRead += [selfreadData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; } // If the paragraph to read is a header paragraphif(_phase == AFHeaderPhase) {NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding]; totalNumberOfBytesRead += [selfreadData:headersData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; } // If the paragraph to read is a content paragraphif(_phase == AFBodyPhase) {// Convert the data stored in the body property to an NSInputStream object and write it to the buffer NSInteger numberOfBytesRead = 0; numberOfBytesRead = [self.inputStreamread:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
        if (numberOfBytesRead == -1) {
            return- 1; }else{ totalNumberOfBytesRead += numberOfBytesRead; // If inputStream is finished, closed, or in error, switch the state machineif([self.inputStream streamStatus] >= NSStreamStatusAtEnd) { [self transitionToNextPhase]; }}} // If the paragraph to be read is the closing boundary paragraphif(_phase == AFFinalBoundaryPhase) {// Generate the corresponding boundary string based on whether there is an end boundary, then generate the corresponding NSData object, Write to butter NSData *closingBoundaryData = ([self hasFinalBoundary]? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]); totalNumberOfBytesRead += [selfreadData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
    }

    returntotalNumberOfBytesRead; } /** Write data from data to buffer */ - (NSInteger)readData:(NSData *)data
           intoBuffer:(uint8_t *)buffer
            maxLength:(NSUInteger)length
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"NSRange = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length] - ((NSUInteger)_phaseReadOffset), length)); // Read and write according to the calculated range [data getBytes:buffer range:range];#pragma clang diagnostic pop_phaseReadOffset += range.length; // If the data in data is finished reading and writing, the state machine is switchedif (((NSUInteger)_phaseReadOffset) >= [data length]) {
        [self transitionToNextPhase];
    }

    return(NSInteger)range.length; } /** Switches to the next paragraph for reading, i.e. controls the state of the state machine */ - (BOOL)transitionToNextPhase {// Switches to the main thread if the method is not called on the main threadif(! [[NSThread currentThread] isMainThread]) { dispatch_sync(dispatch_get_main_queue(), ^{ [self transitionToNextPhase]; });return YES;
    }

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcovered-switch-default"Switch (_phase) {// If you are reading the middle boundary paragraph, then read the header paragraphcase AFEncapsulationBoundaryPhase:
            _phase = AFHeaderPhase;
            break; // If the header paragraph is read now, the next step is to read the content paragraph, initialize inputStream, add it to the current running loop, and start itcase AFHeaderPhase:
            [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
            [self.inputStream open];
            _phase = AFBodyPhase;
            break; // If you are reading a content paragraph, then read the end boundary paragraph and close the inputStreamcase AFBodyPhase:
            [self.inputStream close];
            _phase = AFFinalBoundaryPhase;
            break; // If you are reading an end boundary paragraph, assign it to the middle boundary paragraphcase AFFinalBoundaryPhase:
        default:
            _phase = AFEncapsulationBoundaryPhase;
            break; } _phaseReadOffset = 0;#pragma clang diagnostic pop

    return YES;
}
Copy the code

6.2.7 Implementation of NSCopying protocol method

- (instancetype)copyWithZone:(NSZone *)zone { AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init]; StringEncoding = self. StringEncoding; bodyPart.headers = self.headers; bodyPart.bodyContentLength = self.bodyContentLength; bodyPart.body = self.body; bodyPart.boundary = self.boundary;return bodyPart;
}
Copy the code

6.3 AFMultipartBodyStream class

The AFMultipartBodyStream class inherits from the NSInputStream class and complies with the NSStreamDelegate protocol. This class holds the data that the user wants to upload and controls the reading of the data as it is uploaded.

6.3.1 Interface

6.3.1.1 properties

/** Size of a single packet */ @property (nonatomic, assign) NSUInteger numberOfBytesInPacket; /** delay */ @property (nonatomic, assign) NSTimeInterval delay; / / @property (nonatomic, strong) NSInputStream *inputStream; /** content size */ @property (readonly, nonatomic, assign) unsigned long long contentLength; /** is null */ @property (readonly, nonatomic, assign, getter = isEmpty) BOOL empty;
Copy the code

6.3.1.2 method

/** initialize with encoding */ - (instancetype)initWithStringEncoding:(NSStringEncoding)encoding; /** Set the start and end boundaries */ - (void)setInitialAndFinalBoundaries; /** Add the AFHTTPBodyPart object */ - (void)appendHTTPBodyPart (AFHTTPBodyPart *)bodyPart;Copy the code

6.3.2 Class extension of NSStream

Because the AFMultipartBodyStream class inherits from NSInputStream, and NSInputStream inherits from NSStream, the streamStatus and streamError properties of NSStream are readOnly, If you want to use read and write properties inside the AFMultipartBodyStream class, you add a class extension to make it private read and write.

@property (readwrite) NSStreamStatus streamStatus;
@property (readwrite, copy) NSError *streamError;
Copy the code

But there’s a problem: The compiler automatically generates getters, setters, and member variables for you by declaring a property via @Property, but the compiler doesn’t automatically generate member variables when a subclass overrides the parent class’s property via @Property. So in the at sign implementation of AFMultipartBodyStream you can see at sign synthesize streamStatus; And @ synthesize streamError; Two lines of code to generate a member variable;

6.3.3 class extensions

/** Code */ @property (readWrite, nonatomic, assign) NSStringEncoding stringEncoding; /** Save an array of afHttpBodyParts */ @property (readWrite, nonatomic, strong) NSMutableArray *HTTPBodyParts; */ @property (readWrite, nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator; /** Current read-write HTTPBodyPart */ @property (readWrite, nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart; / / @property (readWrite, nonatomic, strong) NSOutputStream *outputStream; /** buffer */ @property (readWrite, nonatomic, strong) NSMutableData *buffer;Copy the code

6.3.4 Implementation

// These three properties are explained in 6.3.2#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wimplicit-atomic-properties"
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1100)
@synthesize delegate;
#endif
@synthesize streamStatus;
@synthesize streamError;
#pragma clang diagnostic pop

- (instancetype)initWithStringEncoding:(NSStringEncoding)encoding {
    self = [super init];
    if(! self) {returnnil; } // Save the parameters passed in and initialize the attribute self.stringencoding = encoding; self.HTTPBodyParts = [NSMutableArray array]; self.numberOfBytesInPacket = NSIntegerMax;return self;
}

- (void)setInitialAndFinalBoundaries {/ / if the property HTTPBodyParts contains element, there will be the first element is set to start boundary, the last element is set to the end of the border, the other elements are set to noif ([self.HTTPBodyParts count] > 0) {
        for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
            bodyPart.hasInitialBoundary = NO;
            bodyPart.hasFinalBoundary = NO;
        }

        [[self.HTTPBodyParts firstObject] setHasInitialBoundary:YES];
        [[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES]; }} - (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart {// add element [self.httpbodyparts] to HTTPBodyParts addObject:bodyPart]; } - (BOOL)isEmpty {// Determine whether there are elements in the HTTPBodyParts attributereturn [self.HTTPBodyParts count] == 0;
}  
Copy the code

6.3.5 Overriding the parent NSInputStream method

- (NSInteger)read(uint8_t *)buffer maxLength (NSUInteger)length {// If the state of the input stream is closed, it endsif ([self streamStatus] == NSStreamStatusClosed) {
        return0; } NSInteger totalNumberOfBytesRead = 0;#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"// As long as the number read is less than the minimum between the number of qualified and the total number of packageswhile((NSUInteger) totalNumberOfBytesRead < MIN (length, the self. NumberOfBytesInPacket)) {/ / if the current HTTPBodyPart is empty or not readable dataif(! self.currentHTTPBodyPart || ! [self.currentHTTPBodyPart hasBytesAvailable]) {// Assign the currentHTTPBodyPart value, but break out of the loop if the next element is emptyif(! (self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {break; } // If the current HTTPBodyPart has a value}else{/ / calculation can also read the maximum number of NSUInteger maxLength = MIN (length, the self. NumberOfBytesInPacket) - (NSUInteger) totalNumberOfBytesRead; // Write data from currentHTTPBodyPart to buffer NSInteger numberOfBytesRead = [self.currentHTTPBodyPartread:&buffer[totalNumberOfBytesRead] maxLength:maxLength]; // If the write failsif(numberOfBytesRead = = 1) {/ / record error and jump out of the loop self. StreamError = self. CurrentHTTPBodyPart. InputStream. StreamError;break;
            } elseTotalNumberOfBytesRead += numberOfBytesRead; // If delay is set, the current thread will be delayed for a period of timeif(self. Delay > 0.0 f) {[NSThread sleepForTimeInterval: self. The delay]; }}}}#pragma clang diagnostic pop

    returntotalNumberOfBytesRead; } - (BOOL)getBuffer:(__unused uint8_t **)buffer length:(__unused NSUInteger *)len {// disable the method of reading cachereturnNO; } - (BOOL)hasBytesAvailable {// If the state is on, data is availablereturn [self streamStatus] == NSStreamStatusOpen;
}

Copy the code

6.3.6 Overriding NSStream method of parent NSInputStream

- (void)open {// If the state of the stream is open, the execution will not continueif (self.streamStatus == NSStreamStatusOpen) {
        return; } // Set the state of the stream to open self.streamStatus = NSStreamStatusOpen; // Set the start and end boundaries [self]setInitialAndFinalBoundaries]; / / initialization HTTPBodyPartEnumerator attribute self. HTTPBodyPartEnumerator = [self. HTTPBodyParts objectEnumerator]; } - (void)close {// Set the state of the stream to close self.streamStatus = NSStreamStatusClosed; } - (id)propertyForKey:(__unused NSString *)key {// disables the query for the key propertyreturn nil;
}

- (BOOL)setProperty:(__unused id)property
             forKey:(__unused NSString *) Key {// disables assignment of the Key attributereturnNO; } - (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoopforMode:(__unused NSString *)mode
{}

- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop
                  forMode:(__unused NSString *) Mode {} - (unsigned long long)contentLength {// iterate over the elements in HTTPBodyParts to calculate the total length unsigned long long length = 0;for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
        length += [bodyPart contentLength];
    }

    return length;
}
Copy the code

6.3.6 Overriding the NSStream private method of NSInputStream

Why override private methods? Since the setHTTPBodyStream method of NSMutableURLRequest accepts an NSInputStream *, if we want to customize NSInputStream, would we just subclass NSInputStream? No, actually use NSMutableURLRequest after doing this request can lead to a crash, tip [xx _scheduleInCFRunLoop: forMode:] : unrecognized selector.

That’s because NSMutableURLRequest actually accepts not an NSInputStream object, but a CoreFoundation CFReadStreamRef object, Because CFReadStreamRef and bridged NSInputStream toll – free, free conversion, but can use CFReadStreamRef CFStreamScheduleWithRunLoop this method, when it calls to this method, Object – c bridging mechanism will invoke the object’s toll – free – c object NSInputStream corresponding function, here is invoked to _scheduleInCFRunLoop: forMode:, if they do not implement this method will crash. The above explanation is taken from this blog post

- (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop
                     forMode:(__unused CFStringRef)aMode
{}

- (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop
                         forMode:(__unused CFStringRef)aMode
{}

- (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags
                 callback:(__unused CFReadStreamClientCallBack)inCallback
                  context:(__unused CFStreamClientContext *)inContext {
    return NO;
}
Copy the code

6.3.7 Implementation of NSCopying protocol method

- (instancetype)copyWithZone:(NSZone *)zone {// copy HTTPBodyParts and set the apocalypse and end boundaries AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding];for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
        [bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]];
    }

    [bodyStreamCopy setInitialAndFinalBoundaries];

    return bodyStreamCopy;
}
Copy the code

6.4 AFStreamingMultipartFormData class

The purpose of this class is to provide an interface for the user to add uploaded data.

When the user adds data, this class converts the data that the user wants to upload into an AFHTTPBodyPart object, which is then stored in its own AFMultipartBodyStream * property. When the data is added, The bodyStream property is assigned to the HTTPBodyStream property of NSMutableURLRequest.

6.4.1 Interface

/** initialize by passing the request and encoding */ - (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest stringEncoding:(NSStringEncoding)encoding; / * * returns the final disposal good NSMutableURLRequest * / - (NSMutableURLRequest *) requestByFinalizingMultipartFormData;Copy the code

6.4.2 Class Extensions

NSMutableURLRequest */ @property (readWrite, nonatomic, copy) NSMutableURLRequest *request; / / @property (readWrite, nonatomic, assign) NSStringEncoding stringEncoding; /** save boundary string */ @property (readwrite, nonatomic, copy) NSString *boundary; */ @property (readWrite, nonatomic, strong) AFMultipartBodyStream *bodyStream;Copy the code

6.4.3 Implementation

- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest
                    stringEncoding:(NSStringEncoding)encoding
{
    self = [super init];
    if(! self) {returnnil; } // Save the parameters passed in and initialize the private property self.request = urlRequest; self.stringEncoding = encoding; self.boundary = AFCreateMultipartFormBoundary(); self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding];returnself; RequestByFinalizingMultipartFormData {} - (NSMutableURLRequest *) / / if there is no data stream is returned directly NSMutableURLRequest objectif ([self.bodyStream isEmpty]) {
        returnself.request; } // Reset the initial and final boundaries to ensure correct content-length [self.bodyStream]setInitialAndFinalBoundaries]; // Assign the data stream to the NSMutableURLRequest object [self.requestsetHTTPBodyStream:self.bodyStream]; // Assign the content-type and Content-Length fields of the request header of the NSMutableURLRequest object [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;
}
Copy the code

6.4.4 Implementation of AFMultipartFormData protocol method

- (BOOL)appendPartWithFileURL:(NSURL *)fileURL name:(NSString *)name error:(NSError * __autoreleasing *)error { // If a parameter is missing in debug mode, crash NSParameterAssert(fileURL) is displayed. NSParameterAssert(name); NSString *fileName = [fileURL lastPathComponent]; // The file path does not contain ". Get the mime type of the file after the suffix nsstrings * mimeType = AFContentTypeForPathExtension ([fileURL pathExtension]); // Call the following methodreturn[self appendPartWithFileURL:fileURL name:name fileName:fileName mimeType:mimeType error:error]; } - (BOOL)appendPartWithFileURL:(NSURL *)fileURL name:(NSString *)name fileName:(NSString *)fileName mimeType:(NSString *)mimeType error:(NSError * __autoreleasing *)error {// in debug mode, NSParameterAssert(fileURL) does not exist; NSParameterAssert(name); NSParameterAssert(fileName); NSParameterAssert(mimeType); // If it is not a valid file pathif(! [fileURL isFileURL]) {/ / will generate an error message assigned to incoming error object pointer returned NSDictionary * the userInfo = @ {NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a file URL"The @"AFNetworking", nil)};
        if (error) {
            *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];
        }

        returnNO; // If the file path is inaccessible}else if([fileURL checkResourceIsReachableAndReturnError: error] = = NO) {/ / will generate an error message assigned to incoming error object pointer returned NSDictionary * the userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"File URL not reachable."The @"AFNetworking", nil)};
        if (error) {
            *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];
        }

        returnNO; } // Get file attributes from the file path, if not, return, NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL Path] error:error];if(! fileAttributes) {returnNO; } // Generate a mutable dictionary to hold information about the request header, And assign NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary] to content-disposition and Content-Type fields; [mutableHeaderssetValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
    [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; // Create an AFHTTPBodyPart object to hold the content to be transferred and add it to the private bodyStream. bodyPart.stringEncoding = self.stringEncoding; bodyPart.headers = mutableHeaders; bodyPart.boundary = self.boundary; bodyPart.body = fileURL; bodyPart.bodyContentLength = [fileAttributes[NSFileSize] unsignedLongLongValue]; [self.bodyStream appendHTTPBodyPart:bodyPart];returnYES; } - (void)appendPartWithInputStream:(NSInputStream *)inputStream name:(NSString *)name fileName:(NSString *)fileName Length :(int64_t)length mimeType:(NSString *)mimeType {// if a parameter is missing in debug mode, crash NSParameterAssert(name) will occur; NSParameterAssert(fileName); NSParameterAssert(mimeType); // Generate a mutable dictionary to hold information about the request header, And assign NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary] to content-disposition and Content-Type fields; [mutableHeaderssetValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
    [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; // Create an AFHTTPBodyPart object to hold the content to be transferred and add it to the private bodyStream. bodyPart.stringEncoding = self.stringEncoding; bodyPart.headers = mutableHeaders; bodyPart.boundary = self.boundary; bodyPart.body = inputStream; bodyPart.bodyContentLength = (unsigned long long)length; [self.bodyStream appendHTTPBodyPart:bodyPart]; } - (void)appendPartWithFileData:(NSData *)data name:(NSString *)name fileName:(NSString *)fileName mimeType:(NSString *)mimeType {// Crash NSParameterAssert(name) when a parameter is missing in debug mode; NSParameterAssert(fileName); NSParameterAssert(mimeType); // Generate a mutable dictionary to hold information about the request header, And assign NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary] to content-disposition and Content-Type fields; [mutableHeaderssetValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
    [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; / / call down below the method [self appendPartWithHeaders: mutableHeaders body: data]; } - (void)appendPartWithFormData:(NSData *)data name:(NSString *)name {// in debug mode, crash occurs when the corresponding parameter is missing NSParameterAssert(name); // Generate a mutable dictionary to hold information about the request header, And assign NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary] to content-disposition and Content-Type fields; [mutableHeaderssetValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"]; / / call the following the method [self appendPartWithHeaders: mutableHeaders body: data]; } - (void)appendPartWithHeaders:(NSDictionary *)headers body:(NSData *)body NSParameterAssert(body); AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; // Create an AFHTTPBodyPart object to hold the content to be transferred and add it to the private bodyStream. bodyPart.stringEncoding = self.stringEncoding; bodyPart.headers = headers; bodyPart.boundary = self.boundary; bodyPart.bodyContentLength = [body length]; bodyPart.body = body; [self.bodyStream appendHTTPBodyPart:bodyPart]; } - (void) throttleBandwidthWithPacketSize: NSUInteger numberOfBytes delay delay: (NSTimeInterval) {/ / set to send a single packet size and request to delay self.bodyStream.numberOfBytesInPacket = numberOfBytes; self.bodyStream.delay = delay; }Copy the code

7. To summarize

Through reading of AFURLRequestSerialization source code, we can see that AFURLRequestSerialization this class was introduced into the various parameters of the use of user to instantiate NSMutableURLRequest object. But there are two parts to this. One is to build normal requests, like GET or POST. The other part is to build the multipart/form-data request.

7.1 Common Request

Normal request process is: set HTTP request header, set some attributes of mutableRequest, parameter encoding, query parameter concatenation

7.1.1 Setting an HTTP Request Header

Set the ACCEPt-language and user-Agent fields of the HTTP request header respectively in the – (instanceType)init method.

In – (nullable NSURLRequest *) requestBySerializingRequest: (NSURLRequest *) request withParameters (nullable id) parameters error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW; Method assigns a user-defined property to the field corresponding to the mutableRequest.

If the request is POST or PUT, In – (NSURLRequest *) requestBySerializingRequest: (NSURLRequest *) request withParameters: (id) the parameters error: (NSError *__autoreleasing *)error set the content-type field.

In addition, users can assign values to Authorization fields through exposed interfaces

7.1.2 Setting some properties of mutableRequest

First, also in the – (instanceType)init method, To its own attributes allowsCellularAccess, cachePolicy, HTTPShouldHandleCookies, HTTPShouldUsePipelining, networkServiceType, timeoutInterva L sets KVO.

Then, in the KVO method callback, listen for which attribute the user defined, saving the corresponding key and value.

– (NSMutableURLRequest *)requestWithMethod:(NSString *)method :(NSString *)URLString parameters:(nullable id)parameters error:(NSError * _Nullable __autoreleasing *)error; The mutableRequest () method assigns value to the key corresponding to the mutableRequest property.

7.1.3 Parameter Coding

Parameter encoding provides three methods, namely key0= Value0 & KEY1 = Value1 percent coding method, JSON coding method and PLIST coding method. These three ways can be instantiated by respectively AFHTTPRequestSerializer objects, AFJSONRequestSerializer and AFPropertyListRequestSerializer object.

When can also by calling the – (void) setQueryStringSerializationWithBlock: (nullable nsstrings * (^) (NSURLRequest * request, id the parameters, NSError * __autoreleasing *error))block; Method to implement block, custom encoding.

However, if the request is GET, HEAD, or DELETE, it can only be encoded by percentage sign encoding and custom encoding

7.1.4 Querying Parameter Stitching

Parameter splicing can be divided into two situations:

First, if the request is GET, HEAD, and DELETE, the processed query parameters are concatenated directly after the URL.

Second, the request method is POST and PUT, which converts the processed parameters into NSData objects and concatenates them into the request body.

7.2 multipart/form – the data request

Multipart/form – the data request is: the process of setting the HTTP request header, set some attributes, instantiation of the mutableRequest AFStreamingMultipartFormData object processing data, read data upload

7.2.1 Setting the HTTP Request Header

With 7.1.1

7.2.2 Setting some properties of mutableRequest

With 7.1.2

7.2.3 instantiation AFStreamingMultipartFormData object data processing

First of all, In – (NSMutableURLRequest *) multipartFormRequestWithMethod: (nsstrings *) method URLString: (URLString nsstrings *) parameters:(nullable NSDictionary

*)parameters constructingBodyWithBlock:(nullable void (^)(id

formData))block error:(NSError * _Nullable __autoreleasing *)error; This method, instantiate the AFStreamingMultipartFormData formData object;

Then, the parameters passed in by the user are parsed into data of type NSData and added to formData.

Then, through the block callback, exposing the formData object to the user, the user through AFStreamingMultipartFormData class provides interface, will want to upload the data added to the formData;

FormData converts the incoming data into an AFHTTPBodyPart object, and then adds it to bodyStream. BodyStream stores the added AFHTTPBodyPart object in an array.

Finally, when the data addition process is complete, formData assigns the bodyStream to the mutableRequest HTTPBodyStream property and waits for the data to be read.

7.2.4 Reading Data during Upload

The NSURLSession object continuously reads data from the HTTPBodyStream property of the NSURLRequest object when sending a request upload, – (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len; Methods;

In the AFMultipartBodyStream class, the method of its parent class is overridden. In this class, the data stored in the AFHTTPBodyPart object is read out by iterating through the data in the HTTPBodyParts property.

– (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)length; And control the processing of different parts of the data through the state machine. The data is read and spliced into formatted strings, and then converted into NSData type data written into buffer.

In addition to that, Can also by calling methods comply with the agent of AFMultipartFormData AFStreamingMultipartFormData class – (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes delay:(NSTimeInterval)delay; To set the maximum size of each packet to be sent and the delay time for each packet to be read.

AFNetworking

AFNetworking (A) — Start using it

Source: reading AFNetworking (2) — AFURLRequestSerialization

Source: reading AFNetworking (3) — AFURLResponseSerialization

AFNetworking (4) — AFSecurityPolicy

Source: reading AFNetworking (5) — AFNetworkReachabilityManager

AFNetworking (6) — Afurlssession Manager

AFNetworking (7) — Afhttpssession Manager

AFNetworking (8) – AFAutoPurgingImageCache

AFNetworking (9) — AFImageDownloader

The source code to read: AFNetworking (10) — AFNetworkActivityIndicatorManager

AFNetworking — UIActivityIndicatorView+AFNetworking

AFNetworking (12) — UIButton+AFNetworking

AFNetworking (13) — UIImageView+AFNetworking

UIProgressView+AFNetworking

AFNetworking — UIRefreshControl+AFNetworking

UIWebView+AFNetworking