该文章阅读的AFNetworking的版本为3.2.0。html
AFHTTPRequestSerializer
这个类是用来构建NSMutableURLRequest
,主要作了请求数据序列化,也就是利用传递来的HTTP请求方法(如:GET)method
、请求URLURLString
和请求参数parameters
来实例化NSMutableURLRequest
类的对象request
git
FOUNDATION_EXPORT NSString * AFPercentEscapedStringFromString(NSString *string);
复制代码
NSString * AFPercentEscapedStringFromString(NSString *string) {
// 在RFC3986的第3.4节中指出,在对查询字段百分号编码时,保留字符中的“?”和“/”能够不用编码,其余的都要进行编码。
static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";
// 获取URL查询字段容许字符,并从中删除除“?”和“/”以外的保留字符
NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
[allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]];
// FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028
// return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
// 每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 pop
NSRange range = NSMakeRange(index, length);
// 每个中文或者英文在NSString中的length均为1,可是一个Emoji的length的长度为2或者4,这是为了不截断Emoji表情产生乱码
// 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;
}
复制代码
FOUNDATION_EXPORT NSString * AFQueryStringFromParameters(NSDictionary *parameters);
复制代码
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
// 把传入的字典转成元素为AFQueryStringPair对象的数组,而后遍历数组将AFQueryStringPair对象转成通过百分号编码的“key=value”类型NSString对象,最后用“&”拼接成一个字符串
NSMutableArray *mutablePairs = [NSMutableArray array];
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
return [mutablePairs componentsJoinedByString:@"&"];
}
复制代码
以上方法调用了如下方法并把parameters
做为参数传递过去github
NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
// 第一个参数key传了nil,第二个参数value传了以上方法传过来的字典
return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}
复制代码
下面这个方法就是对字典进行处理,转成元素为AFQueryStringPair
对象的数组json
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
// 设置排序描述为按照对象的description属性的字母升序排列
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
// 若是参数value传入的是NSDictionary
if ([value isKindOfClass:[NSDictionary class]]) {
// 声明变量保存传入的字典
NSDictionary *dictionary = value;
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
// 将字典的key按照首字母升序排列后进行遍历
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
// 若是遍历出的key所对应的value不为空,就递归调用本方法,若是有key值则传(key[nestedKey], nestedValue),不然传(nestedKey, nestedValue)
id nestedValue = dictionary[nestedKey];
if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
}
// 若是参数value传入的是NSArray
} else if ([value isKindOfClass:[NSArray class]]) {
// 声明变量保存传入的数组
NSArray *array = value;
// 遍历数组
for (id nestedValue in array) {
// 递归调用本方法,若是有key值则传递(key[], nestedValue),不然传((null)[], nestedValue)
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}
// 若是参数value传入的是NSSet
} else if ([value isKindOfClass:[NSSet class]]) {
// 声明变量保存传入的集合
NSSet *set = value;
// 将集合的元素按照首字母升序排列后进行遍历
for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
// 递归调用本方法,若是有key值则传(key, obj),不然传((null), obj)
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
}
// 若是参数value传入的不是集合对象
} else {
// 利用传入的参数key和value实例化AFQueryStringPair对象并添加到mutableQueryStringComponents数组中
[mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
}
// 返回由字典对象转化元素为AFQueryStringPair对象组成的数组
return mutableQueryStringComponents;
}
复制代码
这个协议定义了一个方法,用来将参parameters
数拼接到NSURLRequest
对象中。其中类AFHTTPRequestSerializer
、AFJSONRequestSerializer
和AFPropertyListRequestSerializer
都遵照这个协议数组
@protocol AFURLRequestSerialization <NSObject, NSSecureCoding, NSCopying>
- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(nullable id)parameters
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
@end
复制代码
这个协议定义了一系列方法用于在- multipartFormRequestWithMethod:parameters:constructingBodyWithBlock:error:
方法中的代码块中为formData
添加数据缓存
/**
将指定路径下数据添加到表单中
*/
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name
error:(NSError * _Nullable __autoreleasing *)error;
/**
将指定路径下数据添加到表单中,并指定文件类型
*/
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType
error:(NSError * _Nullable __autoreleasing *)error;
/**
将指定输入流中的数据添加到表单中
*/
- (void)appendPartWithInputStream:(nullable NSInputStream *)inputStream
name:(NSString *)name
fileName:(NSString *)fileName
length:(int64_t)length
mimeType:(NSString *)mimeType;
/**
将指定NSData对象添加到表单中,并指定文件类型
*/
- (void)appendPartWithFileData:(NSData *)data
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType;
/**
将指定NSData对象添加到表单中
*/
- (void)appendPartWithFormData:(NSData *)data
name:(NSString *)name;
/**
将指定的请求头和请求体添加到表单中
*/
- (void)appendPartWithHeaders:(nullable NSDictionary <NSString *, NSString *> *)headers
body:(NSData *)body;
/**
经过设置请求的带宽和延迟时间来提升在弱网环境下上传数据的成功率
*/
- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes
delay:(NSTimeInterval)delay;
复制代码
紧接着能够看到一个枚举,定义了请求查询字段的编码方式,不过目前只定义了一种默认方式bash
typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) {
AFHTTPRequestQueryStringDefaultStyle = 0,
};
复制代码
能够在.h
文件中看到一共有三个类,分别是AFHTTPRequestSerializer
和它的两个子类AFJSONRequestSerializer
、AFPropertyListRequestSerializer
,先来看一下AFHTTPRequestSerializer
这个类服务器
先在.h
文件中看一下对外暴漏的接口部分cookie
/**
字符串编码方式,默认为NSUTF8StringEncoding
*/
@property (nonatomic, assign) NSStringEncoding stringEncoding;
/**
是否容许使用蜂窝网,默认为是
*/
@property (nonatomic, assign) BOOL allowsCellularAccess;
/**
请求的缓存策略
*/
@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;
/**
是否将cookies添加到request的header中一同发送给服务器,默认为是
*/
@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;
/**
是否使用管线化,便是否要等到收到前一个请求的响应后才能发送后一个请求,管线化能够一个发送一组请求,没必要等待,默认为否
*/
@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;
/**
网络服务类型,系统会根据设置的类型自动优化
*/
@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;
/**
超时时长,默认为60秒
*/
@property (nonatomic, assign) NSTimeInterval timeoutInterval;
/**
请求头信息
*/
@property (readonly, nonatomic, strong) NSDictionary <NSString *, NSString *> *HTTPRequestHeaders;
复制代码
/**
实例化默认设置对象的方法
*/
+ (instancetype)serializer;
/**
设置请求头的字段和值,若是值为nil就移除该字段
*/
- (void)setValue:(nullable NSString *)value
forHTTPHeaderField:(NSString *)field;
/**
获取请求头指定字段的值
*/
- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field;
/**
利用帐号和密码为请求头的“Authorization”字段赋值
*/
- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
password:(NSString *)password;
/**
从请求头中清除“Authorization”字段的值
*/
- (void)clearAuthorizationHeader;
/**
要把查询字符串编码拼接到URL后面的HTTP请求方法集合,默认为GET、HEAD和DELETE
*/
@property (nonatomic, strong) NSSet <NSString *> *HTTPMethodsEncodingParametersInURI;
/**
设置查询字符串的编码方法,目前AFNetworking只实现了一种,即百分号编码
*/
- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style;
/**
设置自定义的查询字符串编码方法,只须要在block中实现编码便可
*/
- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;
/**
利用传入的HTTP请求方法、请求URL和请求参数三个参数生成NSMutableURLRequest对象。当HTTP请求方法为GET、HEAD或DELETE时,参数会拼接到URL后面,不然,就添加到请求体中
*/
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(nullable id)parameters
error:(NSError * _Nullable __autoreleasing *)error;
/**
利用传入的HTTP请求方法、请求URL和请求参数三个参数生成multipart/form-dat请求的NSMutableURLRequest对象。
*/
- (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;
/**
移除掉原request中的HTTPBodyStream,并异步写到指定路径下,并返回NSMutableURLRequest对象
*/
- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request
writingStreamContentsToFile:(NSURL *)fileURL
completionHandler:(nullable void (^)(NSError * _Nullable error))handler;
@end
复制代码
看完了接口部分,再进入.m
文件中看一下私有实现网络
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;
}
复制代码
该方法经过一个单例模式获取须要观察的AFHTTPRequestSerializer
对象的属性,并保存在一个数组中返回。
static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerObserverContext;
复制代码
该变量用于识别观察者的身份
@interface AFHTTPRequestSerializer ()
// 用来保存须要观察的用户自定义的AFHTTPRequestSerializer对象的属性
@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;
// 用来保存请求头信息
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;
// 用来保存查询字段编码类型
@property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle;
// 用来保存用户自定义的查询字段编码方式代码块
@property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization;
@end
复制代码
+ (instancetype)serializer {
// 就是正常的实例化方法
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
// 初始化字符串编码方式为NSUTF8StringEncoding
self.stringEncoding = NSUTF8StringEncoding;
// 初始化请求头
self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];
// 获取前五个用户偏好的语言并赋值给请求头Accept-Language字段
NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
[[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
float q = 1.0f - (idx * 0.1f);
[acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];
*stop = q <= 0.5f;
}];
[self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];
// 获取项目名称(若是没有则获取BundleID)、应用Version版本号(若是没有则获取应用Build版本号)、设备类型、系统版本号和屏幕缩放比并赋值给请求头User-Agent字段
NSString *userAgent = nil;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
#if TARGET_OS_IOS
// User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
#elif TARGET_OS_WATCH
// User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
userAgent = [NSString stringWithFormat:@"%@/%@ (%@; watchOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[WKInterfaceDevice currentDevice] model], [[WKInterfaceDevice currentDevice] systemVersion], [[WKInterfaceDevice currentDevice] screenScale]];
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]];
#endif
#pragma clang diagnostic pop
if (userAgent) {
// 若是不能进行无损ASCII编码,即不是只有普通的字符或ASCII码
if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
NSMutableString *mutableUserAgent = [userAgent mutableCopy];
// 若是移除全部非ASCII值范围的全部字符,移除后再次赋值
if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) {
userAgent = mutableUserAgent;
}
}
[self setValue:userAgent forHTTPHeaderField:@"User-Agent"];
}
// HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
// 初始化须要把查询字符串编码拼接到URL后面的HTTP请求方法集合为GET、HEAD和DELETE方法
self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
// 初始化要观察的自定义的AFHTTPRequestSerializer属性集合
self.mutableObservedChangedKeyPaths = [NSMutableSet set];
// 遍历AFHTTPRequestSerializer须要添加观察的属性,添加观察者,并设置上下文为AFHTTPRequestSerializerObserverContext用于标识
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
}
}
return self;
}
- (void)dealloc {
// 遍历AFHTTPRequestSerializer须要添加观察的属性,移除观察者
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
[self removeObserver:self forKeyPath:keyPath context:AFHTTPRequestSerializerObserverContext];
}
}
}
复制代码
- (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))];
}
复制代码
- (NSDictionary *)HTTPRequestHeaders {
// 返回私有属性mutableHTTPRequestHeaders
return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
}
- (void)setValue:(NSString *)value
forHTTPHeaderField:(NSString *)field
{
// 为私有属性mutableHTTPRequestHeaders赋值
[self.mutableHTTPRequestHeaders setValue:value forKey:field];
}
- (NSString *)valueForHTTPHeaderField:(NSString *)field {
// 获取私有属性mutableHTTPRequestHeaders指定key的值
return [self.mutableHTTPRequestHeaders valueForKey:field];
}
- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
password:(NSString *)password
{
// 先把帐户和密码拼接成一个字符串后转为UTF8格式的NSData对象,再经过base64编码成字符串赋值给请求头的Authorization字段
NSData *basicAuthCredentials = [[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0];
[self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] forHTTPHeaderField:@"Authorization"];
}
- (void)clearAuthorizationHeader {
// 从请求头中移除Authorization字段
[self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"];
}
- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style {
// 若是设置了编码格式就把自定义编码代码块置nil
self.queryStringSerializationStyle = style;
self.queryStringSerialization = nil;
}
- (void)setQueryStringSerializationWithBlock:(NSString *(^)(NSURLRequest *, id, NSError *__autoreleasing *))block {
// 这是为了用户在设置代码块时有智能提示,能够直接回车敲出
self.queryStringSerialization = block;
}
复制代码
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
// 在debug模式下缺乏对应参数会crash
NSParameterAssert(method);
NSParameterAssert(URLString);
// 利用传入的路径生成NSURL对象
NSURL *url = [NSURL URLWithString:URLString];
// 判断url是否生成
NSParameterAssert(url);
// 利用生成的NSURL对象生成NSMutableURLRequest对象,并设置请求方式
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
// 遍历AFHTTPRequestSerializer须要添加观察的属性
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
// 若是遍历出的属性是用户自定义的属性
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
// 将属性对应的值赋值给NSMutableURLRequest对象相对应的属性
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
// 将传入的参数parameters处理后添加到mutableRequest中
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
// 返回mutableRequest
return mutableRequest;
}
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(NSDictionary *)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
error:(NSError *__autoreleasing *)error
{
// 没有传请求方法就crash
NSParameterAssert(method);
// 请求方法是GET或HEAD就crash
NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);
// 调用上个公共方法生成NSMutableURLRequest对象
NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];
// 利用NSMutableURLRequest对象生成AFStreamingMultipartFormData对象formData
__block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];
// 若是传递了参数
if (parameters) {
// 将传入的字典参数转为元素是AFQueryStringPair对象的数组,并进行遍历
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
// 将对象pair的value属性转为NSData对象,并拼到formData对象中
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]];
}
}
}
// 调用代码块拼接想要上传的数据
if (block) {
block(formData);
}
// 构建multipart/form-data请求独有的请求头
return [formData requestByFinalizingMultipartFormData];
}
- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request
writingStreamContentsToFile:(NSURL *)fileURL
completionHandler:(void (^)(NSError *error))handler
{
// request对象的HTTPBodyStream属性为nil则crash
NSParameterAssert(request.HTTPBodyStream);
// fileURL不是合法的文件路径则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), ^{
// 把输入输出流添加到默认模式的当前运行循环中
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
// 打开输入输出流
[inputStream open];
[outputStream open];
// 若是输入输出流还有可操做字节
while ([inputStream hasBytesAvailable] && [outputStream hasSpaceAvailable]) {
uint8_t buffer[1024];
// 每次从输入流中读取最大1024bytes大小的数据存入buffer中,若是出错则跳出循环
NSInteger bytesRead = [inputStream read: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 (bytesRead == 0 && bytesWritten == 0) {
break;
}
}
// 关闭输入输出流
[outputStream close];
[inputStream close];
// 若是传入了回调代码块则在主队列异步回调
if (handler) {
dispatch_async(dispatch_get_main_queue(), ^{
handler(error);
});
}
});
// 把原mutableRequest对象的HTTPBodyStream属性置nil后返回
NSMutableURLRequest *mutableRequest = [request mutableCopy];
mutableRequest.HTTPBodyStream = nil;
return mutableRequest;
}
复制代码
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
// 缺乏request则会crash
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
// 遍历request的请求头,对没有值的字段进行赋值
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
// 对参数parameters进行编码
NSString *query = nil;
if (parameters) {
// 若是用户自定义了编码代码块则用用户自定义的方法编码
if (self.queryStringSerialization) {
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);
if (serializationError) {
if (error) {
*error = serializationError;
}
return nil;
}
// 若是用户没有自定义编码代码块则用AFNetworking默认的编码方式,即百分号编码
} else {
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
// 若是HTTP请求方法为GET、HEAD或DELETE其中之一
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
// 就把查询字符串拼接到url后面
if (query && query.length > 0) {
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}
// 若是HTTP请求方法为POST、PUT其中之一
} else {
// 就把查询字符串拼接到请求体中
// #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]];
}
// 返回拼接好参数的mutableRequest对象
return mutableRequest;
}
复制代码
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
// 若是是须要观察的AFHTTPRequestSerializer对象的属性,则不自动实现KVO
if ([AFHTTPRequestSerializerObservedKeyPaths() containsObject:key]) {
return NO;
}
return [super automaticallyNotifiesObserversForKey:key];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(__unused id)object
change:(NSDictionary *)change
context:(void *)context
{
// 若是观察到的是AFHTTPRequestSerializer类添加观察的属性
if (context == AFHTTPRequestSerializerObserverContext) {
// 若是给当前属性赋的值不为null就添加到self.mutableObservedChangedKeyPaths中,不然从其中移除
if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
[self.mutableObservedChangedKeyPaths removeObject:keyPath];
} else {
[self.mutableObservedChangedKeyPaths addObject:keyPath];
}
}
}
复制代码
在iOS6中,苹果引入了一个新的协议,是基于NSCoding
的,叫作NSSecureCoding
。NSSecureCoding
和NSCoding
是同样的,除了在解码时要同时指定key和要解码的对象的类,若是要求的类和从文件中解码出的对象的类不匹配,NSCoder
会抛出异常,告诉你数据已经被篡改了。
+ (BOOL)supportsSecureCoding {
// 若是一个类符合 NSSecureCoding 协议并在 + supportsSecureCoding 返回 YES,就声明了它能够处理自己实例的编码解码方式,以防止替换攻击。
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))];
}
复制代码
- (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
复制代码
AFJSONRequestSerializer
是AFHTTPRequestSerializer
的子类,当服务器要求咱们上传的数据格式类型为json时,就可使用此类
// 设置JSON的编码类型
@property (nonatomic, assign) NSJSONWritingOptions writingOptions;
复制代码
// 实例化工厂方法
+ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions;
复制代码
+ (instancetype)serializer {
// 调用下面的方法并传默认的JSON输出格式
return [self serializerWithWritingOptions:(NSJSONWritingOptions)0];
}
+ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions
{
// 调用父类的初始化方法并保存了传入的参数
AFJSONRequestSerializer *serializer = [[self alloc] init];
serializer.writingOptions = writingOptions;
return serializer;
}
复制代码
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
// 缺乏request则会crash
NSParameterAssert(request);
// 若是HTTP请求方法为GET、HEAD或DELETE其中之一
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
// 就直接调用父类的实现并返回
return [super requestBySerializingRequest:request withParameters:parameters error:error];
}
NSMutableURLRequest *mutableRequest = [request mutableCopy];
// 遍历request的请求头,对没有值的字段进行赋值
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
// 若是传入了参数
if (parameters) {
// 若是mutableRequest的请求头的Content-Type字段没有值
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
// 为mutableRequest的请求头的Content-Type字段赋值为application/json
[mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
}
// 将传入的parameters转成JSON格式的NSData对象并添加到mutableRequest的请求体中
[mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];
}
return mutableRequest;
}
复制代码
就是在父类的基础上添加了writingOptions属性
- (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))];
}
复制代码
一样也是在父类的基础上添加了writingOptions属性
- (instancetype)copyWithZone:(NSZone *)zone {
AFJSONRequestSerializer *serializer = [super copyWithZone:zone];
serializer.writingOptions = self.writingOptions;
return serializer;
}
复制代码
AFPropertyListRequestSerializer
是AFHTTPRequestSerializer
的子类,此类能够把传入的参数编码成plist格式NSDate对象传给服务器,通常是用来向服务器传递XML格式的数据
// plist输出格式
@property (nonatomic, assign) NSPropertyListFormat format;
// plist编码类型,目前这个值尚未用
@property (nonatomic, assign) NSPropertyListWriteOptions writeOptions;
复制代码
// 实例化工厂方法
+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format
writeOptions:(NSPropertyListWriteOptions)writeOptions;tions;
复制代码
+ (instancetype)serializer {
// 调用下面的实例化方法,设置plist的输出格式为XML类型
return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 writeOptions:0];
}
+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format
writeOptions:(NSPropertyListWriteOptions)writeOptions
{
// 调用父类的初始化方法并保存了传入的参数
AFPropertyListRequestSerializer *serializer = [[self alloc] init];
serializer.format = format;
serializer.writeOptions = writeOptions;
return serializer;
}
复制代码
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
// 缺乏request则会crash
NSParameterAssert(request);
// 若是HTTP请求方法为GET、HEAD或DELETE其中之一
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
// 就直接调用父类的实现并返回
return [super requestBySerializingRequest:request withParameters:parameters error:error];
}
NSMutableURLRequest *mutableRequest = [request mutableCopy];
// 遍历request的请求头,对没有值的字段进行赋值
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
// 若是传入了参数
if (parameters) {
// 若是mutableRequest的请求头的Content-Type字段没有值
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
// // 为mutableRequest的请求头的Content-Type字段赋值application/x-plist
[mutableRequest setValue:@"application/x-plist" forHTTPHeaderField:@"Content-Type"];
}
// 将传入的parameters转成plist格式的NSData对象并添加到mutableRequest的请求体中
[mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]];
}
return mutableRequest;
}
复制代码
/**
AFURLRequestSerializer类的错误,错误码对应NSURLErrorDomain的错误码
*/
FOUNDATION_EXPORT NSString * const AFURLRequestSerializationErrorDomain;
/**
这个key只存在AFURLRequestSerializationErrorDomain中,其对应的值是NSURLRequest错误请求的操做
*/
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLRequestErrorKey;
/**
HTTP请求输入流的节流带宽字节数的最大分组大小。等于16KB。
*/
FOUNDATION_EXPORT NSUInteger const kAFUploadStream3GSuggestedPacketSize;
/**
HTTP请求输入流的节流带宽每次读取数据包时的延迟时间。等于0.2秒。
*/
FOUNDATION_EXPORT NSTimeInterval const kAFUploadStream3GSuggestedDelay;
复制代码
在对请求的查询参数编码时,传入的参数会被拆分红最小的集合类对象,而后将集合对象转成AFQueryStringPair
对象,再对field和value百分号编码后生成field=value
类型的字符串
@property (readwrite, nonatomic, strong) id field; // 字段
@property (readwrite, nonatomic, strong) id value; // 值
复制代码
/**
AFQueryStringPair对象初始化方法
@param field 字段
@param value 值
@return 初始化的AFQueryStringPair对象
*/
- (instancetype)initWithField:(id)field value:(id)value;
/**
将属性field和value进行百分号编码后,之间用”=“拼接成一个字符串
@return 处理好的字符串
*/
- (NSString *)URLEncodedStringValue;
复制代码
- (instancetype)initWithField:(id)field value:(id)value {
self = [super init];
if (!self) {
return nil;
}
// 属性保存初始化传入的参数
self.field = field;
self.value = value;
return self;
}
- (NSString *)URLEncodedStringValue {
// 若是value值为nil或null
if (!self.value || [self.value isEqual:[NSNull null]]) {
// 只把属性field的字符串描述属性进行百分号编码后返回
return AFPercentEscapedStringFromString([self.field description]);
// 若是value值不为nil或null
} else {
// 把属性field和value进行百分号编码后,之间用”=“拼接成一个字符串返回
return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
}
}
复制代码
每个AFHTTPBodyPart
就是表明一项表单数据,即一个要上传的文件的数据,并由它本身读取它内部的数据
/**
回车换行
*/
static NSString * const kAFMultipartFormCRLF = @"\r\n";
/**
3G环境上传建议带宽
*/
NSUInteger const kAFUploadStream3GSuggestedPacketSize = 1024 * 16;
/**
3G环境上传建议延时
*/
NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
复制代码
/**
由随机生成的八位16进制字符串组成的边界字符串
*/
static NSString * AFCreateMultipartFormBoundary() {
return [NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()];
}
/**
生成开始边界字符串
*/
static inline NSString * AFMultipartFormInitialBoundary(NSString *boundary) {
return [NSString stringWithFormat:@"--%@%@", boundary, kAFMultipartFormCRLF];
}
/**
生成中间边界字符串
*/
static inline NSString * AFMultipartFormEncapsulationBoundary(NSString *boundary) {
return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF];
}
/**
生成结束边界字符串
*/
static inline NSString * AFMultipartFormFinalBoundary(NSString *boundary) {
return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF];
}
/**
根据文件后缀名获取文件的MIME类型,即Content-Type字段的值
*/
static inline NSString * AFContentTypeForPathExtension(NSString *extension) {
// 经过传入的文件后缀字符串生成一个UTI字符串(统一类型标识符是惟一标识抽象类型的字符串。它们能够用来描述文件格式或内存中的数据类型,但也能够用来描述其余类型的实体类型,如目录,卷或包。)
NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
// 将UTI转成MIME类型
NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
if (!contentType) {
return @"application/octet-stream";
} else {
return contentType;
}
}
复制代码
/**
编码方式
*/
@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;
/**
是否有开始边界
*/
@property (nonatomic, assign) BOOL hasInitialBoundary;
/**
是否有结束边界
*/
@property (nonatomic, assign) BOOL hasFinalBoundary;
/**
内容长度
*/
@property (readonly, nonatomic, assign, getter = hasBytesAvailable) BOOL bytesAvailable;
/**
内容长度
*/
@property (readonly, nonatomic, assign) unsigned long long contentLength;
复制代码
/**
将AFHTTPBodyPart对象中的数据读出,并写入到buffer中,也就是AFHTTPBodyPart对象本身把本身保存的数据读取出来,而后写入到传递进来的参数buffer中
*/
- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)length;
复制代码
typedef enum {
AFEncapsulationBoundaryPhase = 1, // 中间边界段落
AFHeaderPhase = 2, // 头段落
AFBodyPhase = 3, // 内容段落
AFFinalBoundaryPhase = 4, // 结束边界段落
} AFHTTPBodyPartReadPhase;
复制代码
/**
保存要读取的段落,其实就是利用状态机模式控制对AFHTTPBodyPart对象不一样内容的读取
*/
AFHTTPBodyPartReadPhase _phase;
/**
保存由AFHTTPBodyPart对象的body属性生成的输入流对象
*/
NSInputStream *_inputStream;
/**
保存当前已读取字节数,用来计算读取进度
*/
unsigned long long _phaseReadOffset;
复制代码
/**
切换到下一段落进行读取,即控制状态机的状态
*/
- (BOOL)transitionToNextPhase;
/**
将AFHTTPBodyPart对象的属性中保存的数据转成的NSDdata对象写入到buffer中
*/
- (NSInteger)readData:(NSData *)data
intoBuffer:(uint8_t *)buffer
maxLength:(NSUInteger)length;
复制代码
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
// 切换到主线程,初始化成员变量_phase为AFEncapsulationBoundaryPhase,_phaseReadOffset为0
[self transitionToNextPhase];
return self;
}
- (void)dealloc {
// 关闭输入流并置空
if (_inputStream) {
[_inputStream close];
_inputStream = nil;
}
}
/**
inputStream的懒加载方法
*/
- (NSInputStream *)inputStream {
if (!_inputStream) {
// 根据body属性的类生成对应的NSInputStream对象并保存
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;
}
/**
将headers属性所保存的字典类型的数据拼接成指定格式的字符串
*/
- (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;
// 若是有开始边界就生成开始边界字符串,不然就生成中间边界字符串,而后生成对应的NSData对象,并获取长度
NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];
length += [encapsulationBoundaryData length];
// 添加header对应的NSData对象的长度
NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];
length += [headersData length];
// 添加body对应的NSData对象的长度
length += _bodyContentLength;
// 若是有结束边界就生成结束边界字符串,不然就生成中间边界字符串,而后生成对应的NSData对象,并获取长度后添加
NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);
length += [closingBoundaryData length];
return length;
}
/**
判断是否有可读数据
*/
- (BOOL)hasBytesAvailable {
// Allows `read:maxLength:` to be called again if `AFMultipartFormFinalBoundary` does not fit into the available buffer
if (_phase == AFFinalBoundaryPhase) {
return YES;
}
// 根据inputStream的属性streamStatus来判断是否有可读数据
#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
}
/**
将自身的数据写入到buffer中
*/
- (NSInteger)read:(uint8_t *)buffer
maxLength:(NSUInteger)length
{
NSInteger totalNumberOfBytesRead = 0;
// 若是要读取的段落是中间边界段落
if (_phase == AFEncapsulationBoundaryPhase) {
// 根据是否有开始边界生成对应的边界字符串,而后生成相应的NSData对象,写入到butter中
NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];
totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
}
// 若是要读取的段落是头部段落
if (_phase == AFHeaderPhase) {
// 将header编码写入到buffer中
NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];
totalNumberOfBytesRead += [self readData:headersData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
}
// 若是要读取的段落是内容段落
if (_phase == AFBodyPhase) {
// 将属性body中保存的数据转为NSInputStream对象再写入到buffer中
NSInteger numberOfBytesRead = 0;
numberOfBytesRead = [self.inputStream read:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
if (numberOfBytesRead == -1) {
return -1;
} else {
totalNumberOfBytesRead += numberOfBytesRead;
// 若是inputStream的状态是结束、关闭或者出错,就切换状态机的状态
if ([self.inputStream streamStatus] >= NSStreamStatusAtEnd) {
[self transitionToNextPhase];
}
}
}
// 若是要读取的段落是结束边界段落
if (_phase == AFFinalBoundaryPhase) {
// 根据是否有结束边界生成对应的边界字符串,而后生成相应的NSData对象,写入到butter中
NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);
totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
}
return totalNumberOfBytesRead;
}
/**
将data中的数据写入到buffer中
*/
- (NSInteger)readData:(NSData *)data
intoBuffer:(uint8_t *)buffer
maxLength:(NSUInteger)length
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
// 计算要读取的范围
NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length] - ((NSUInteger)_phaseReadOffset), length));
// 根据计算好的范围读写
[data getBytes:buffer range:range];
#pragma clang diagnostic pop
// 记录读写的进度
_phaseReadOffset += range.length;
// 若是data中的数据读写完成,就切换状态机的状态
if (((NSUInteger)_phaseReadOffset) >= [data length]) {
[self transitionToNextPhase];
}
return (NSInteger)range.length;
}
/**
切换到下一段落进行读取,即控制状态机的状态
*/
- (BOOL)transitionToNextPhase {
// 若是该方法不是在主线程调用,就切换到主线程
if (![[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) {
// 若是如今读取的是中间边界段落,接下来就要读取头部段落
case AFEncapsulationBoundaryPhase:
_phase = AFHeaderPhase;
break;
// 若是如今读取的是头部段落,接下来就要读取内容段落,初始化inputStream添加到当前运行循环中,并开启
case AFHeaderPhase:
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.inputStream open];
_phase = AFBodyPhase;
break;
// 若是如今读取的是内容段落,接下来就要读取结束边界段落,关闭inputStream
case AFBodyPhase:
[self.inputStream close];
_phase = AFFinalBoundaryPhase;
break;
// 若是如今读取的是结束边界段落,就赋值为中间边界段落
case AFFinalBoundaryPhase:
default:
_phase = AFEncapsulationBoundaryPhase;
break;
}
// 段落读取偏移量置零
_phaseReadOffset = 0;
#pragma clang diagnostic pop
return YES;
}
复制代码
- (instancetype)copyWithZone:(NSZone *)zone {
AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init];
// 复制了主要属性
bodyPart.stringEncoding = self.stringEncoding;
bodyPart.headers = self.headers;
bodyPart.bodyContentLength = self.bodyContentLength;
bodyPart.body = self.body;
bodyPart.boundary = self.boundary;
return bodyPart;
}
复制代码
AFMultipartBodyStream
类继承自NSInputStream
类,并遵照了NSStreamDelegate
协议。这个类保存着用户要上传的数据,并在数据上传时控制数据的读取。
/**
单个包的大小
*/
@property (nonatomic, assign) NSUInteger numberOfBytesInPacket;
/**
延时
*/
@property (nonatomic, assign) NSTimeInterval delay;
/**
输入流
*/
@property (nonatomic, strong) NSInputStream *inputStream;
/**
内容大小
*/
@property (readonly, nonatomic, assign) unsigned long long contentLength;
/**
是否为空
*/
@property (readonly, nonatomic, assign, getter = isEmpty) BOOL empty;
复制代码
/**
经过编码方式初始化
*/
- (instancetype)initWithStringEncoding:(NSStringEncoding)encoding;
/**
设置开始和结束边界
*/
- (void)setInitialAndFinalBoundaries;
/**
添加AFHTTPBodyPart对象
*/
- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart;
复制代码
由于AFMultipartBodyStream
类继承自NSInputStream
类,而NSInputStream
继承自NSStream
类,但NSStream
类的streamStatus
属性和streamError
属性是readonly
,想要在AFMultipartBodyStream
类内部使用读写这两个属性,因而添加了类扩展,改成私有可读写的。
@property (readwrite) NSStreamStatus streamStatus;
@property (readwrite, copy) NSError *streamError;
复制代码
但这样会出现一个问题:本来只要经过@property
声明属性,编译器就会自动帮咱们生成getter
、setter
和成员变量,可是子类经过@property
覆盖了父类的属性,这时编译器就不会自动生成成员变量,所以在AFMultipartBodyStream
类的@implementation
中能够看到@synthesize streamStatus;
和@synthesize streamError;
两句代码来生成成员变量;
/**
编码方式
*/
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
/**
保存AFHTTPBodyPart的数组
*/
@property (readwrite, nonatomic, strong) NSMutableArray *HTTPBodyParts;
/**
保存对属性HTTPBodyParts内容的遍历
*/
@property (readwrite, nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator;
/**
当前读写的HTTPBodyPart
*/
@property (readwrite, nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart;
/**
输出流
*/
@property (readwrite, nonatomic, strong) NSOutputStream *outputStream;
/**
缓冲
*/
@property (readwrite, nonatomic, strong) NSMutableData *buffer;
复制代码
// 这三个属性在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) {
return nil;
}
// 保存传入的参数和初始化属性
self.stringEncoding = encoding;
self.HTTPBodyParts = [NSMutableArray array];
self.numberOfBytesInPacket = NSIntegerMax;
return self;
}
- (void)setInitialAndFinalBoundaries {
// 若是属性HTTPBodyParts内有元素,就将第一个元素设置为有开始边界,最后一个元素设置为有结束边界,其余元素都设置为无
if ([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 {
// 向HTTPBodyParts属性内添加元素
[self.HTTPBodyParts addObject:bodyPart];
}
- (BOOL)isEmpty {
// 判断HTTPBodyParts属性内是否有元素
return [self.HTTPBodyParts count] == 0;
}
复制代码
- (NSInteger)read:(uint8_t *)buffer
maxLength:(NSUInteger)length
{
// 若是输入流的状态是关闭就结束
if ([self streamStatus] == NSStreamStatusClosed) {
return 0;
}
// 定义变量记录已读取总数
NSInteger totalNumberOfBytesRead = 0;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
// 只要已读取的数量小于限定的数量和包的总数量两者中的最小值
while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) {
// 若是当前HTTPBodyPart为空或者没有可读数据
if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {
// 为currentHTTPBodyPart赋值,但若是下一个元素为空则跳出循环
if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {
break;
}
// 若是当前HTTPBodyPart有值
} else {
// 计算还能读取的最大数量
NSUInteger maxLength = MIN(length, self.numberOfBytesInPacket) - (NSUInteger)totalNumberOfBytesRead;
// 将currentHTTPBodyPart中的数据写入到buffer中
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;
}
- (BOOL)getBuffer:(__unused uint8_t **)buffer
length:(__unused NSUInteger *)len
{
// 关闭读取缓存的方法
return NO;
}
- (BOOL)hasBytesAvailable {
// 只要状态为开就是有数据
return [self streamStatus] == NSStreamStatusOpen;
}
复制代码
- (void)open {
// 若是流的状态是打开就不继续执行
if (self.streamStatus == NSStreamStatusOpen) {
return;
}
// 将流的状态设置为打开
self.streamStatus = NSStreamStatusOpen;
// 设置开始和结束边界
[self setInitialAndFinalBoundaries];
// 初始化HTTPBodyPartEnumerator属性
self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator];
}
- (void)close {
// 将流的状态设置为关闭
self.streamStatus = NSStreamStatusClosed;
}
- (id)propertyForKey:(__unused NSString *)key {
// 关闭对key属性的查询
return nil;
}
- (BOOL)setProperty:(__unused id)property
forKey:(__unused NSString *)key
{
// 关闭对key属性的赋值
return NO;
}
// 将设置和移除运行环境的方法设置为何都不作
- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop
forMode:(__unused NSString *)mode
{}
- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop
forMode:(__unused NSString *)mode
{}
- (unsigned long long)contentLength {
// 遍历HTTPBodyParts中的元素计算总长度
unsigned long long length = 0;
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
length += [bodyPart contentLength];
}
return length;
}
复制代码
为何要重写私有方法?由于NSMutableURLRequest
的setHTTPBodyStream
方法接受的是一个NSInputStream *
参数,那咱们要自定义NSInputStream
的话,建立一个NSInputStream
的子类传给它是否是就能够了?实际上不行,这样作后用NSMutableURLRequest
发出请求会致使crash,提示[xx _scheduleInCFRunLoop:forMode:]: unrecognized selector
。
这是由于NSMutableURLRequest
实际上接受的不是NSInputStream
对象,而是CoreFoundation
的CFReadStreamRef
对象,因为CFReadStreamRef
和NSInputStream
是toll-free bridged
,能够自由转换,但CFReadStreamRef
会用到CFStreamScheduleWithRunLoop
这个方法,当它调用到这个方法时,object-c
的toll-free bridging
机制会调用object-c
对象NSInputStream
的相应函数,这里就调用到了_scheduleInCFRunLoop:forMode:
,若不实现这个方法就会crash。以上解释摘自这篇博客
- (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;
}
复制代码
- (instancetype)copyWithZone:(NSZone *)zone {
// 拷贝了HTTPBodyParts并设置了启示和结束边界
AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding];
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
[bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]];
}
[bodyStreamCopy setInitialAndFinalBoundaries];
return bodyStreamCopy;
}
复制代码
这个类的做用是提供接口以便用户添加上传的数据。
当用户添加数据时,该类会将用户想要上传的数据分别转成AFHTTPBodyPart
对象,而后依次保存到自身AFMultipartBodyStream *
类型的属性bodyStream
中,当数据添加完成,就会将属性bodyStream
赋值给NSMutableURLRequest
的HTTPBodyStream
属性。
/**
经过传递请求和编码方式进行初始化
*/
- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest
stringEncoding:(NSStringEncoding)encoding;
/**
返回最终处理好的NSMutableURLRequest
*/
- (NSMutableURLRequest *)requestByFinalizingMultipartFormData;
复制代码
/**
保存传入的NSMutableURLRequest对象
*/
@property (readwrite, nonatomic, copy) NSMutableURLRequest *request;
/**
保存传入的编码方式
*/
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
/**
保存边界字符串
*/
@property (readwrite, nonatomic, copy) NSString *boundary;
/**
保存输入数据流
*/
@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream;
复制代码
- (instancetype)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;
}
- (NSMutableURLRequest *)requestByFinalizingMultipartFormData {
// 若是没有数据流就直接返回NSMutableURLRequest对象
if ([self.bodyStream isEmpty]) {
return self.request;
}
// 设置数据流的开始和结束边界
// Reset the initial and final boundaries to ensure correct Content-Length
[self.bodyStream setInitialAndFinalBoundaries];
// 将数据流赋值给NSMutableURLRequest对象
[self.request setHTTPBodyStream:self.bodyStream];
// 为NSMutableURLRequest对象的请求头的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;
}
复制代码
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name
error:(NSError * __autoreleasing *)error
{
// 在debug模式下缺乏对应参数会crash
NSParameterAssert(fileURL);
NSParameterAssert(name);
// 经过文件的路径中获取带有后缀的文件名
NSString *fileName = [fileURL lastPathComponent];
// 经过文件的路径获取不带“.”的后缀名后获取文件的mime类型
NSString *mimeType = AFContentTypeForPathExtension([fileURL pathExtension]);
// 调用下面那个方法
return [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
{
// 在debug模式下缺乏对应参数会crash
NSParameterAssert(fileURL);
NSParameterAssert(name);
NSParameterAssert(fileName);
NSParameterAssert(mimeType);
// 若是不是一个合法的文件路径
if (![fileURL isFileURL]) {
// 就生成一个错误信息赋值给传入的错误对象指针后返回
NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil)};
if (error) {
*error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];
}
return NO;
// 若是文件路径没法访问
} else if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) {
// 就生成一个错误信息赋值给传入的错误对象指针后返回
NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"File URL not reachable.", @"AFNetworking", nil)};
if (error) {
*error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];
}
return NO;
}
// 经过文件路径获取文件的属性,若是获取不到则返回,由于没法获取到文件的大小
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:error];
if (!fileAttributes) {
return NO;
}
// 生成一个可变字典保存请求头的相关信息,并为Content-Disposition和Content-Type字段赋值
NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
[mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
[mutableHeaders setValue:mimeType forKey:@"Content-Type"];
// 生成一个AFHTTPBodyPart对象保存要传输的内容,并添加到私有属性bodyStream中
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
bodyPart.stringEncoding = self.stringEncoding;
bodyPart.headers = mutableHeaders;
bodyPart.boundary = self.boundary;
bodyPart.body = fileURL;
bodyPart.bodyContentLength = [fileAttributes[NSFileSize] unsignedLongLongValue];
[self.bodyStream appendHTTPBodyPart:bodyPart];
return YES;
}
- (void)appendPartWithInputStream:(NSInputStream *)inputStream
name:(NSString *)name
fileName:(NSString *)fileName
length:(int64_t)length
mimeType:(NSString *)mimeType
{
// 在debug模式下缺乏对应参数会crash
NSParameterAssert(name);
NSParameterAssert(fileName);
NSParameterAssert(mimeType);
// 生成一个可变字典保存请求头的相关信息,并为Content-Disposition和Content-Type字段赋值
NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
[mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
[mutableHeaders setValue:mimeType forKey:@"Content-Type"];
// 生成一个AFHTTPBodyPart对象保存要传输的内容,并添加到私有属性bodyStream中
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
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
{
// 在debug模式下缺乏对应参数会crash
NSParameterAssert(name);
NSParameterAssert(fileName);
NSParameterAssert(mimeType);
// 生成一个可变字典保存请求头的相关信息,并为Content-Disposition和Content-Type字段赋值
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];
}
- (void)appendPartWithFormData:(NSData *)data
name:(NSString *)name
{
// 在debug模式下缺乏对应参数会crash
NSParameterAssert(name);
// 生成一个可变字典保存请求头的相关信息,并为Content-Disposition和Content-Type字段赋值
NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
[mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"];
// 调用下面的那个方法
[self appendPartWithHeaders:mutableHeaders body:data];
}
- (void)appendPartWithHeaders:(NSDictionary *)headers
body:(NSData *)body
{
// 在debug模式下缺乏对应参数会crash
NSParameterAssert(body);
// 生成一个AFHTTPBodyPart对象保存要传输的内容,并添加到私有属性bodyStream中
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
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:(NSTimeInterval)delay
{
// 设置发送单个包的大小和请求延迟
self.bodyStream.numberOfBytesInPacket = numberOfBytes;
self.bodyStream.delay = delay;
}
复制代码
经过对AFURLRequestSerialization
源码的阅读,能够看出AFURLRequestSerialization
这个类是利用用户传入的各类参数来实例化NSMutableURLRequest
对象。但这还分为两个部分,一个部分是构建普通的请求:如GET
或POST
;另外一部分是构建multipart/form-data
请求。
普通请求的过程是:设置HTTP请求头、设置mutableRequest
的一些属性、参数编码、查询参数拼接
在- (instancetype)init
方法中分别设置HTTP请求头Accept-Language
字段和User-Agent
字段。
在- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(nullable id)parameters error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
方法中将用户自定义的属性赋值给mutableRequest
对应的字段。
若是请求方式是POST
或者PUT
,还会在- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error
方法中设置Content-Type
字段。
除此以外,用户还能够经过暴露的接口为Authorization
字段赋值
首先,一样在- (instancetype)init
方法中,对自身的属性allowsCellularAccess
、cachePolicy
、HTTPShouldHandleCookies
、HTTPShouldUsePipelining
、networkServiceType
、timeoutInterval
设置了KVO。
而后,在KVO方法回调中监听用户自定义了哪一个属性,保存对应的key
和value
。
最后在- (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(nullable id)parameters error:(NSError * _Nullable __autoreleasing *)error;
方法中将value
赋值给mutableRequest
对应的key
,也就是为mutableRequest
的属性赋值。
参数编码提供了三种方式,分别是key0=value0&key1=value1
百分号编码方式、json
编码方法和plist
编码方式,这三种方式能够经过分别实例化AFHTTPRequestSerializer
对象、AFJSONRequestSerializer
对象和AFPropertyListRequestSerializer
对象来实现。
当时也能够经过调用- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;
方法,实现block
,来自定义编码方式。
不过若是请求方式是GET
、HEAD
或者DELETE
,只能经过百分号编码方式和自定义编码方式来进行编码
参数拼接分为两种状况:
第一种,若是请求方式是GET
、HEAD
和DELETE
,就把处理好的查询参数直接拼接到url
后面;
第二种,请求方式是POST
和PUT
,就把处理好的参数转成NSData
对象后拼接到请求体中。
multipart/form-data
请求的过程是:设置HTTP请求头、设置mutableRequest
的一些属性、实例化AFStreamingMultipartFormData
对象处理数据、上传时读取数据
同7.1.1
同7.1.2
首先,在- (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;
这个方法中,实例化了AFStreamingMultipartFormData
对象formData
;
而后,将用户传入的参数通过解析后转为NSData
类型的数据,添加到formData
中;
接着,经过block
回调,将formData
对象暴露给用户,用户经过AFStreamingMultipartFormData
类提供的接口,将想要上传的数据添加到formData
中;
其中,formData
会将传入的数据,分别转成AFHTTPBodyPart
对象,而后依次添加到自身属性bodyStream
中,bodyStream
会将添加进来的AFHTTPBodyPart
对象用数组保存起来;
最后,当数据添加处理完成,formData
就会将bodyStream
赋值给mutableRequest``HTTPBodyStream
属性,等待数据的读取。
当发送请求上传时,NSURLSession
对象会不断读取NSURLRequest
对象的属性HTTPBodyStream
中的数据,在读取数据时会调用NSInputStream
对象的- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len;
方法;
在AFMultipartBodyStream
类中,重写了其父类的这个方法,在这其中,经过遍历属性HTTPBodyParts
中的数据,将AFHTTPBodyPart
对象中保存的数据读取出来;
AFHTTPBodyPart
对象经过调用- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)length;
方法,并经过状态机控制对不一样部分的数据的处理,数据边读取边拼接成带有格式的字符串,而后再转换成NSData
类型的数据写入到buffer中;
除此以外,还能够经过调用AFStreamingMultipartFormData
类遵照的代理AFMultipartFormData
中的方法- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes delay:(NSTimeInterval)delay;
来设置在上传数据时,每次发送数据包的最大大小和每次读取数据包的延迟时间。
源码阅读系列:AFNetworking
源码阅读:AFNetworking(二)——AFURLRequestSerialization
源码阅读:AFNetworking(三)——AFURLResponseSerialization
源码阅读:AFNetworking(四)——AFSecurityPolicy
源码阅读:AFNetworking(五)——AFNetworkReachabilityManager
源码阅读:AFNetworking(六)——AFURLSessionManager
源码阅读:AFNetworking(七)——AFHTTPSessionManager
源码阅读:AFNetworking(八)——AFAutoPurgingImageCache
源码阅读:AFNetworking(九)——AFImageDownloader
源码阅读:AFNetworking(十)——AFNetworkActivityIndicatorManager
源码阅读:AFNetworking(十一)——UIActivityIndicatorView+AFNetworking
源码阅读:AFNetworking(十二)——UIButton+AFNetworking
源码阅读:AFNetworking(十三)——UIImageView+AFNetworking
源码阅读:AFNetworking(十四)——UIProgressView+AFNetworking