AFNetworking 3.0源码阅读 - AFURLResponseSerialization
?這次來說一下AFURLResponseSerialization這個HTTP響應類。
定義了一個協議,該協議返回序列化后的結果。后續的AFHTTPResponseSerializer以及他的子類都遵循了該協議
該類內有很多子類定義,這里借用一張圖來展示,之后一個一個來說。
我們先來看下AFHTTPResponseSerializer在頭文件中的定義有哪些。
可以看到有兩個初始化方法,然后stringEncoding后面有一個宏,這個宏代表這棄用了。
?
?來看實現文件的部分。
這里有一個NSIndexSet。
NSIndexSet:代表一個不可變的獨特的無符號整數的集合,稱為索引,因為使用它們的方式。這個集合被稱為索引集。你不應該使用索引集存儲任意集合的整數值,因為索引集按索引排序范圍的方式存儲的。這使得它們更有效率比存儲單個整數的集合。這也意味著每個索引值指數中只能出現一次。通俗點講NSIndexSet就是一個唯一的,有序的,無符號整數的集合。
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];表示接受狀態碼范圍200~299。
- (BOOL)validateResponse:(NSHTTPURLResponse *)responsedata:(NSData *)dataerror:(NSError * __autoreleasing *)error {BOOL responseIsValid = YES;//默認為yesNSError *validationError = nil;//存在response且為NSHTTPURLResponse類if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {//條件:1.acceptableContentTypes存在 2.response的MIMEType不在范圍內 3.MIMEType和data均不為空if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&!([response MIMEType] == nil && [data length] == 0)) {//data長度大于0且respone的URL存在if ([data length] > 0 && [response URL]) {//定義錯誤信息字典NSMutableDictionary *mutableUserInfo = [@{NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],NSURLErrorFailingURLErrorKey:[response URL],AFNetworkingOperationFailingURLResponseErrorKey: response,} mutableCopy];if (data) {//錯誤信息包含datamutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;}//生成NSerrorvalidationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);}responseIsValid = NO;}if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {NSMutableDictionary *mutableUserInfo = [@{NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],NSURLErrorFailingURLErrorKey:[response URL],AFNetworkingOperationFailingURLResponseErrorKey: response,} mutableCopy];if (data) {mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;}validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);responseIsValid = NO;}}if (error && !responseIsValid) {*error = validationError;}return responseIsValid; }response有效性驗證過程。
NSSecureCoding, NSCopying協議方法。
?
進入該屬性類型NSJSONReadingOptions可以看到是下圖這么定義的。
-
NSJSONReadingMutableContainers:返回是可變容器類型如NSMutableArray,NSMutableDictionay。
-
NSJSONReadingMutableLeaves:返回的字符串是NSMutableSting。
-
NSJSONReadingAllowFragments:json結構最外層可以不是NSArray和NSDictionay。
?
接著來看對應的實現文件部分。
三種初始化方法從init方法中可以看到acceptableContentTypes支持"application/json", "text/json", "text/javascript"三種。
- (id)responseObjectForResponse:(NSURLResponse *)responsedata:(NSData *)dataerror:(NSError *__autoreleasing *)error {//response有效性無效if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {//無error或者code為NSURLErrorCannotDecodeContentData解析無效,要返回nilif (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {return nil;}}// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.// See https://github.com/rails/rails/issues/1742//data由空格字符構成也需要返回nilBOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];if (data.length == 0 || isSpace) {return nil;}NSError *serializationError = nil;id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];if (!responseObject){if (error) {*error = AFErrorWithUnderlyingError(serializationError, *error);}return nil;}if (self.removesKeysWithNullValues) {return AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);}return responseObject; }轉化和解析方法,這里又出現了兩個定義的C函數貼在下面。
static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {//error的domain和code與入參對影響等直接返回yesif ([error.domain isEqualToString:domain] && error.code == code) {return YES;} else if (error.userInfo[NSUnderlyingErrorKey]) {//否則判斷userInfo中key為NSUnderlyingErrorKey是否有值,有遞歸調用自身return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);}return NO; } static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {//數組類if ([JSONObject isKindOfClass:[NSArray class]]) {//定義可變數組NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];for (id value in (NSArray *)JSONObject) { [mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];}return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];} else if ([JSONObject isKindOfClass:[NSDictionary class]]) {//同上NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {id value = (NSDictionary *)JSONObject[key];//去除value為NSNull的keyif (!value || [value isEqual:[NSNull null]]) {[mutableDictionary removeObjectForKey:key];} else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);}}return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];}return JSONObject; }?
這里有點好奇為什么不把數組里面的NSNull對象也去掉而只是把字典中value為NSNull的key去掉。
NSSecureCoding, NSCopying協議方法。
?
解析XML的子類。
不多說比較好理解。
?
將JSON轉換為PropertyList用到的子類。
ContentType支持application/x-plist。
?
這里是UIImage的一個分類,從方法名字上看可以想到是一個安全的NSData轉Image的方法。
通過NSLock來解決多線程上的一些安全問題。
static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) {UIImage *image = [UIImage af_safeImageWithData:data];if (image.images) {return image;}return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation]; }static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) {if (!data || [data length] == 0) {return nil;}CGImageRef imageRef = NULL;CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);if ([response.MIMEType isEqualToString:@"image/png"]) {imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault);} else if ([response.MIMEType isEqualToString:@"image/jpeg"]) {imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault);if (imageRef) {CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef);CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace);// CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so fall back to AFImageWithDataAtScaleif (imageColorSpaceModel == kCGColorSpaceModelCMYK) {CGImageRelease(imageRef);imageRef = NULL;}}}CGDataProviderRelease(dataProvider);UIImage *image = AFImageWithDataAtScale(data, scale);if (!imageRef) {if (image.images || !image) {return image;}imageRef = CGImageCreateCopy([image CGImage]);if (!imageRef) {return nil;}}size_t width = CGImageGetWidth(imageRef);size_t height = CGImageGetHeight(imageRef);size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);if (width * height > 1024 * 1024 || bitsPerComponent > 8) {CGImageRelease(imageRef);return image;}// CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreatesize_t bytesPerRow = 0;CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace);CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);if (colorSpaceModel == kCGColorSpaceModelRGB) {uint32_t alpha = (bitmapInfo & kCGBitmapAlphaInfoMask); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wassign-enum"if (alpha == kCGImageAlphaNone) {bitmapInfo &= ~kCGBitmapAlphaInfoMask;bitmapInfo |= kCGImageAlphaNoneSkipFirst;} else if (!(alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast)) {bitmapInfo &= ~kCGBitmapAlphaInfoMask;bitmapInfo |= kCGImageAlphaPremultipliedFirst;} #pragma clang diagnostic pop}CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo);CGColorSpaceRelease(colorSpace);if (!context) {CGImageRelease(imageRef);return image;}CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), imageRef);CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context);CGContextRelease(context);UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation];CGImageRelease(inflatedImageRef);CGImageRelease(imageRef);return inflatedImage; }上面的這些個方法涉及圖形圖像和CoreGraphics,不是很懂。。以后再補吧,先把代碼貼在這里。
?
?
?
最后還有個AFCompoundResponseSerializer,也比較簡單不常用不多說了。
?
通過該篇記錄下之后需要學習的東西
?
轉載于:https://www.cnblogs.com/kaisi/p/9442724.html
總結
以上是生活随笔為你收集整理的AFNetworking 3.0源码阅读 - AFURLResponseSerialization的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ABB机器人的错误处理
- 下一篇: 区块链学习(1) sha256算法 c