才开始是看AF的源码,挺尴尬的,根据自己的理解记录自己看源码学习到的一些东西,主要是一些常用的,第一次写,主要是为了记录,怕自己以后忘了,有错误欢迎指出
AFURLRequestSerialization
这一模块主要是写的是NSURLRequest,用来创建网络请求的管理器;
@protocol
@protocol AFURLRequestSerialization <NSObject, NSSecureCoding, NSCopying>
/**
Returns a request with the specified parameters encoded into a copy of the original request.
@param request The original request.
@param parameters The parameters to be encoded.
@param error The error that occurred while attempting to encode the request parameters.
@return A serialized request.
*/
- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(nullable id)parameters
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
@end
这个主要是用来将parameters解析成query使用的。
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
NSString *query = nil;
if (parameters) {
if (self.queryStringSerialization) {
NSError *serializationError;
//用户自己通过block设置query
query = self.queryStringSerialization(request, parameters, &serializationError);
if (serializationError) {
if (error) {
*error = serializationError;
}
return nil;
}
} else {
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
//设置query的方法
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
if (query && query.length > 0) {
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}
} else {
// #2864: an empty string is a valid x-www-form-urlencoded payload
if (!query) {
query = @"";
}
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
将parameters转换成queryString的方法
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
return [mutablePairs componentsJoinedByString:@"&"];
}
NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
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
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
id nestedValue = dictionary[nestedKey];
if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
}
} else if ([value isKindOfClass:[NSArray class]]) {
NSArray *array = value;
for (id nestedValue in array) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}
} else if ([value isKindOfClass:[NSSet class]]) {
NSSet *set = value;
for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
}
} else {
[mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
}
return mutableQueryStringComponents;
}
上面第一个方法使用了AFQueryStringPair类和第二个方法,然后第二个方法又使用了第三个方法。那么我们先来看第三个方法。
第三个方法实际上就是对parameters进行了逐层解析,最终解析成key和value,并将其存入到了AFQueryStringPair对象中,返回了一个存有AFQueryStringPair对象的数组。
这个方法 [[AFQueryStringPair alloc] initWithField:key value:value]
的作用是将字典中的键值对储存为类的两个属性。
那么回到第一个方法,[pair URLEncodedStringValue]];
AFQueryStringPair的这个方法是之前存储的两个属性转换成key=value格式的字符串,变成query的格式。
所以
以上三个方法的作用就是将parameters装换成key=value&key1=value1
的query类型字符串
@interface
定义了一些属性,对系统一些属性通过KVO进行了观察,当发生改变时,就会将其
存储,在创建urlrequest时将其设置为HTTPHeaderField
property (nonatomic, assign) NSStringEncoding stringEncoding;//编码格式
@property (nonatomic, strong) NSSet <NSString *> *HTTPMethodsEncodingParametersInURI;
@property (readonly, nonatomic, strong) NSDictionary <NSString *, NSString *> *HTTPRequestHeaders;//请求头
#pragma mark 系统的URLRequest的一些属性
@property (nonatomic, assign) BOOL allowsCellularAccess;//是否创建能使用设备蜂窝网络的请求,默认为yes
@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;//缓存策略 默认NSURLRequestUseProtocolCachePolicy
@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;//是否使用默认的cookie处理,default yes
@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;//在接受到早期传输的响应之前,创建的请求是否可以继续传输数据。是否收到响应再继续传输新请求 default no
@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;//网络服务类型,default NSURLNetworkServiceTypeDefault
@property (nonatomic, assign) NSTimeInterval timeoutInterval;//请求时间,默认60秒
初始化方法
+ (instancetype)serializer;
//设置请求头key value,如果为空,删除请求头中的现有值
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;
//根据key获取请求头value
- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field;
//身份验证
- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
password:(NSString *)password;
//清除验证
- (void)clearAuthorizationHeader;
//设置querystyle,目前只有一种
- (void)setQueryStringSerializationWithStyle:(XDWHTTPRequestQueryStringSerializationStyle)style;//设置style,只有一种0
//手动设置query
- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;
//获得需要的URLRequest
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(nullable id)parameters
error:(NSError * _Nullable __autoreleasing *)error;
扩展
@interface XDWHTTPRequestSerializer ()
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;//可变请求头字典
@property (readwrite, nonatomic, strong) dispatch_queue_t requestHeaderModificationQueue;//请求头修改队列
@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;//集合 存储KVO观察的方法
@property (readwrite, nonatomic, assign) XDWHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle;//querystyle
@property (readwrite, nonatomic, copy) XDWQueryStringSerializationBlock queryStringSerialization;//修改query的block
@end
@implementation
初始化方法
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.stringEncoding = NSUTF8StringEncoding;
self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];
self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT);
self.mutableObservedChangedKeyPaths = [NSMutableSet set];
//Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
[[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
float q = 1.0f - (idx * 0.1f);
[acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];
*stop = q <= 0.5f;
}];
[self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];
//User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
NSString *userAgent = nil;
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]];
if (userAgent) {
if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
NSMutableString *mutableUserAgent = [userAgent mutableCopy];
if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) {
userAgent = mutableUserAgent;
}
}
[self setValue:userAgent forHTTPHeaderField:@"User-Agent"];
}
//KVO,观察系统NSMutableRequest属性
for (NSString *keyPath in XDWHTTPRequestSerializerObservedKeyPaths()) {
if ([self respondsToSelector:@selector(keyPath)]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:XDWHTTPRequestSerializerObserverContext];//设置上线文,更好区分
}
}
return self;
}
KVO的例子,手动设置KVO需要实现下面的方法,automaticallyNotifiesObserversForKey
以及willChangeValueForKey:
和 didChangeValueForKey
这里设置KVO是为了,如果更改了KVO相关的参数,就是添加到集合中,在创建request时会添加为request的HeaderField中
#pragma mark -- KVO
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
if ([XDWHTTPRequestSerializerObservedKeyPaths() containsObject:key]) {
return NO;
}
return [super automaticallyNotifiesObserversForKey:key];
}
- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess {
[self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
_allowsCellularAccess = allowsCellularAccess;
[self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
}
HTTPHeader 设置
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field {
dispatch_barrier_async(self.requestHeaderModificationQueue, ^{
[self.mutableHTTPRequestHeaders setValue:value forKey:field];
});
}
- (NSDictionary<NSString *,NSString *> *)HTTPRequestHeaders {
__block NSDictionary *value = nil;
dispatch_sync(self.requestHeaderModificationQueue, ^{
value = [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
});
return value;
}
- (NSString *)valueForHTTPHeaderField:(NSString *)field {
__block NSString *value = nil;
dispatch_sync(self.requestHeaderModificationQueue, ^{
value = [self.HTTPRequestHeaders objectForKey:field];
});
return value;
}
此处使用了一个线程队列进行修改,设置时使用barrier,避免出现同时操作的情况出现,保证了线程安全,用sync同步取值,保证了是当前线程取到的数据不会被其他的操作更改。保证了数据的时效性准确性.(在并发队列中执行同步操作,如果牵扯到有异步操作正在操作的对象,会阻塞线程进行等待,等这个操作完成后,即之前的所有与这个对象有关的操作完成后,再进行同步操作中与此对象相关的操作,取消线程阻塞,执行后续的操作)