博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
AFNetworking 3.0 源码解读(四)之 AFURLResponseSerialization
阅读量:7077 次
发布时间:2019-06-28

本文共 19972 字,大约阅读时间需要 66 分钟。

本篇是AFNetworking 3.0 源码解读的第四篇了。

 

这次主要讲AFURLResponseSerialization(HTTP响应)这一个类的知识。

这是一个协议,只要遵守这个协议,就要实现NSSecureCoding/NSCopying这两个协议,还要实现

- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response                           data:(nullable NSData *)data                          error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;

这个方法来返回序列化后的结果。不管是下边的AFHTTPResponseSerializer,还是它的子类,都遵守这个协议,也就是在各自的实现中实现了这个协议,然后返回了属于自身的一个结果。

 

ps:根据这个协议,我有了一些启发。当我们在设计一个网络框架的时候,因为业务不同,返回的数据也有很多种,通常的一种做法是直接返回服务器响应的数据,由业务人员自己实现业务。但是如果业务繁杂,这样写出的代码也会很乱,我们不妨采用类似这种协议的设计模式,这样做有两个好处:

1. 业务人员和数据人员可以分开。 数据提前约定好名称和内容,写数据人员实现数据部分,写业务人员实现业务部分。

2. 左右的数据转换放到协议实现方法中,出现问题,更容易查找问题。

 

 

由于这个类有很多的子类,我们先来看看这些类的组成,然后逐一的对每个子类的代码进行解读。

 

==================================  分割线 =======================================

 我们还是先来看看AFHTTPResponseSerializer头文件的组成部分。

 

 

 

 

 

来看看实现部分:

这个初始化,看起来还是很普通的,主要是初始化一些默认值,我们在这里花点篇幅讲解一下NSIndexSet这个集合的知识。

定义:NSIndexSet是一个有序的,唯一的,无符号整数的集合。

我们先看个例子:

1    NSMutableIndexSet *indexSetM = [NSMutableIndexSet indexSet]; 2     [indexSetM addIndex:19]; 3     [indexSetM addIndex:4]; 4     [indexSetM addIndex:6]; 5     [indexSetM addIndex:8]; 6     [indexSetM addIndexesInRange:NSMakeRange(20, 10)]; 7      8     [indexSetM enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { 9         NSLog(@"%lu",idx);10     }];

打印结果如下:

2016-08-10 11:39:00.826 qikeyunDemo[3765:100078] 42016-08-10 11:39:00.827 qikeyunDemo[3765:100078] 62016-08-10 11:39:00.827 qikeyunDemo[3765:100078] 82016-08-10 11:39:00.827 qikeyunDemo[3765:100078] 192016-08-10 11:39:00.827 qikeyunDemo[3765:100078] 202016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 212016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 222016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 232016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 242016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 252016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 262016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 272016-08-10 11:39:00.828 qikeyunDemo[3765:100078] 282016-08-10 11:39:00.829 qikeyunDemo[3765:100078] 29

这充分说明了一下几点:

1. 它是一个无符号整数的集合。

2. 使用addIndex方法可以添加单个整数值,使用addIndexesInRange可以添加一个范围,比如NSMakeRange(20, 10) 取20包括20后边十个整数。

3. 唯一性,集合内的整数不会重复出现。

具体用法就不再次做详细的解释了,有兴趣的朋友可以看看这篇文章: 

 

因此 self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)]; 这个方法设置了默认接受的状态码范围为200 ~ 299.

 

1 - (BOOL)validateResponse:(NSHTTPURLResponse *)response 2                     data:(NSData *)data 3                    error:(NSError * __autoreleasing *)error 4 { 5     // 1. 默认responseIsValid == yes 6     BOOL responseIsValid = YES; 7     NSError *validationError = nil; 8  9     // 2. 假如response存在且类型是NSHTTPURLResponse10     if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {11         12         // 2.1 条件:self.acceptableContentTypes存在且不包含服务器返回的MIMEType且MIMEType和data都不能为空13         if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&14             !([response MIMEType] == nil && [data length] == 0)) {15 16             if ([data length] > 0 && [response URL]) {17                 18                 // 2.1.1 生成错误信息19                 NSMutableDictionary *mutableUserInfo = [@{20                                                           NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],21                                                           NSURLErrorFailingURLErrorKey:[response URL],22                                                           AFNetworkingOperationFailingURLResponseErrorKey: response,23                     24                                                           } mutableCopy];25                 // 2.1.2 包含data错误信息26                 if (data) {27                     mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;28                 }29 30                 // 2.1.3 生成NSError31                 validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);32             }33 34             responseIsValid = NO;35         }36 37         // 同上38         if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {39             NSMutableDictionary *mutableUserInfo = [@{40                                                NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],41                                                NSURLErrorFailingURLErrorKey:[response URL],42                                                AFNetworkingOperationFailingURLResponseErrorKey: response,43                                        } mutableCopy];44 45             if (data) {46                 mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;47             }48 49             // 设置错误,通过AFErrorWithUnderlyingError这个函数设置validationError的NSUnderlyingErrorKey50             validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);51 52             responseIsValid = NO;53         }54     }55 56     // 赋值57     if (error && !responseIsValid) {58         *error = validationError;59     }60 61     return responseIsValid;62 }

这个方法就是检测响应的有效性的。默认是YES。整个方法中比较值得关注的是对NSError的使用。在这里不对它做详细的介绍,大概解释下最长用的一些东东。

1. 我们关注下它的NSDictionary *userInfo这个属性,错误信息一般都在这个字典中获得。因为是一个字典,所以我们在给NSError的userInfo赋值的时候,会用到key。我们看看系统自带的key和含义有哪些?

 

当然我们也可以自定义key来操作NSError。

在上边的那个方法中,有可能会出现两个错误,在self.acceptableContentTypes和self.acceptableStatusCodes这两个判断中,如果都出现错误怎么办呢?

这就用到了NSUnderlyingErrorKey 这个字段,它标示一个优先的错误,value为NSErro对象。

通过下边的这个函数进行了转换和赋值:

 

 

1 #pragma mark - NSSecureCoding 2  3 + (BOOL)supportsSecureCoding { 4     return YES; 5 } 6  7 - (instancetype)initWithCoder:(NSCoder *)decoder { 8     self = [self init]; 9     if (!self) {10         return nil;11     }12 13     self.acceptableStatusCodes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableStatusCodes))];14     self.acceptableContentTypes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableContentTypes))];15 16     return self;17 }18 19 - (void)encodeWithCoder:(NSCoder *)coder {20     [coder encodeObject:self.acceptableStatusCodes forKey:NSStringFromSelector(@selector(acceptableStatusCodes))];21     [coder encodeObject:self.acceptableContentTypes forKey:NSStringFromSelector(@selector(acceptableContentTypes))];22 }23 24 #pragma mark - NSCopying25 26 - (instancetype)copyWithZone:(NSZone *)zone {27     AFHTTPResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];28     serializer.acceptableStatusCodes = [self.acceptableStatusCodes copyWithZone:zone];29     serializer.acceptableContentTypes = [self.acceptableContentTypes copyWithZone:zone];30 31     return serializer;32 }

这几个是对 NSCopying NSSecureCoding 这两个协议的实现部分,算是固定写法吧。为了节省篇幅 ,在下边的各个分类中,就不对这些代码进行说明了。

 

==================================  分割线 ==================================

我们来看看这个Json序列化的头文件。

 

 

这个选项可以设置json的读取选项,我们点进去可以看到:

  • NSJSONReadingMutableContainers    这个解析json成功后返回一个容器。

  • NSJSONReadingMutableLeaves         返回中的json对象中字符串为NSMutableString

  • NSJSONReadingAllowFragments        允许JSON字符串最外层既不是NSArray也不是NSDictionary,但必须是有效的JSON Fragment。例如使用这个选项可以解析 @“123” 这样的字符串

我们举个例子说明一下NSJSONReadingMutableContainers:

1 NSString *str = @"{\"name\":\"zhangsan\"}"; 2      3     NSMutableDictionary *dict = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]; 4     // 应用崩溃,不选用NSJSONReadingOptions,则返回的对象是不可变的,NSDictionary 5     [dict setObject:@"male" forKey:@"sex"]; 6      7     NSMutableDictionary *dict = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil]; 8     // 没问题,使用NSJSONReadingMutableContainers,则返回的对象是可变的,NSMutableDictionary 9     [dict setObject:@"male" forKey:@"sex"];10     11     NSLog(@"%@", dict);

如果服务器返回的json的最外层并不是以NSArray 或者 NSDictionary ,而是一个有效的json fragment ,比如 就返回了一个@"abc"。 那么我们使用NSJSONReadingAllowFragments这个选项也能够解析出来。

 

 

这个属性用来设置是否过滤NSNull。

 

 

通过初始化方法可以看出来,AFNetworking支持的ContentType有:

  • application/json

  • text/json

  • text/javascript

 

1 - (id)responseObjectForResponse:(NSURLResponse *)response 2                            data:(NSData *)data 3                           error:(NSError *__autoreleasing *)error 4 { 5     // 判空处理,如果验证结果失败,在没有error 或者 错误中code:NSURLErrorCannotDecodeContentData 的情况下,是不能解析数据的,就返回nil 6     if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) { 7         if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) { 8             return nil; 9         }10     }11 12     id responseObject = nil;13     NSError *serializationError = nil;14     // 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.15     // See https://github.com/rails/rails/issues/174216     BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];17     if (data.length > 0 && !isSpace) {18         responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];19     } else {20         return nil;21     }22 23     if (self.removesKeysWithNullValues && responseObject) {24         responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);25     }26 27     if (error) {28         *error = AFErrorWithUnderlyingError(serializationError, *error);29     }30 31     return responseObject;32     33     34 }

json转化的和新方法,这个方法中除了加了一些必要的判断之外,新出现了两个函数,函数的实现也比较好理解,就不做详细介绍了,把代码贴出来:

1 // 检测错误或者有限错误中是否匹配code和domain 2 static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) { 3      4     // error中的domain和code相同,直接返回YES 5     if ([error.domain isEqualToString:domain] && error.code == code) { 6         return YES; 7          8         // 否则检测error中的NSUnderlyingErrorKey 9     } else if (error.userInfo[NSUnderlyingErrorKey]) {10         return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);11     }12 13     return NO;14 }
1 // 该方法用于删除一个对象中的NUll 2 static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) { 3      4     // 1.数组 5     if ([JSONObject isKindOfClass:[NSArray class]]) { 6          7         //1.1 创建一个可变的数组,为了提高性能,使用Capacity创建 8         NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]]; 9         10         // 1.2 遍历数组,通过迭代的手段清空数组内的null11         for (id value in (NSArray *)JSONObject) {12             [mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];13         }14 15         // 1.3 readingOptions == NSJSONReadingMutableContainers 返回可变容器,其他返回不可变容器16         return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];17     }18     19     // 2. 思想同上20     else if ([JSONObject isKindOfClass:[NSDictionary class]]) {21         NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];22         for (id 
key in [(NSDictionary *)JSONObject allKeys]) {23 id value = (NSDictionary *)JSONObject[key];24 if (!value || [value isEqual:[NSNull null]]) {25 [mutableDictionary removeObjectForKey:key];26 } else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {27 mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);28 }29 }30 31 return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];32 }33 34 return JSONObject;35 }

 

================================ 分割线 ====================================

这个分类是解析XML数据的。

 

支持的ContentType:

  • application/xml

  • text/xml

 

================================  分割线 =============================

 

注: 这个子类只在mac os x上使用

支持的ContentType:

  • application/xml

  • text/xml

 

 

=============================== 分隔线 =================================

这个分类用来把json解析成PropertyList:

支持的ContentType:

  • application/x-plist

 

=================================  分割线 ===============================

这是一个UIImage分类,添加了一个安全的NData转UIImage的方法;那么什么叫安全的呢?当我们访问或者操纵数据的时候,由于数据还可能被别人操纵,这就有可能出现不安全的情况,为了解决这个问题,引入NSLock这个锁对象 。 

使用方法:

  • 创建一个单例NSLock *lock
  • [lock lock]
  • do something...
  • [lock unlock]

 

这个私有方法,返回一个按照scale收缩的图片。这就使用到了上边的那个安全转换数据的方法了。但看这个方法我们应该知道下边两点知识:

  • image.images   images这个属性的用法,下边我们举例说明
  • - (instancetype)initWithCGImage:(CGImageRef)cgImage scale:(CGFloat)scale orientation:(UIImageOrientation)orientation NS_AVAILABLE_IOS(4_0);   这个是创建UIImage的一个方法,稍微注意下需要哪些参数。

1 UIImage *image0 = [UIImage imageNamed:@"SenderVoiceNodePlaying001"];2     UIImage *image1 = [UIImage imageNamed:@"SenderVoiceNodePlaying002"];3     UIImage *image2 = [UIImage imageNamed:@"SenderVoiceNodePlaying003"];4     5     self.imageView.image = [UIImage animatedImageWithImages:@[image0,image1,image2] duration:1.5];6

效果图如下:

那么这个images属性就可以应用到很多地方了,我们可以使用这个属性来生成一个类似gif的效果或者简单的动画是最常用的场景。

 

1 // 根据响应结果和scale返回一张图片  2 static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) {  3       4     // 1. 判空  5     if (!data || [data length] == 0) {  6         return nil;  7     }  8   9     // 2. 但凡带CG开头的,标示是CoreGraphics 10     CGImageRef imageRef = NULL; 11      12     // 3. CGDataProviderRef 可以细节为CoreGraphics中对data的包装 13     CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); 14  15     // 判断响应返回的MIMEType类型, 16     if ([response.MIMEType isEqualToString:@"image/png"]) { 17         imageRef = CGImageCreateWithPNGDataProvider(dataProvider,  NULL, true, kCGRenderingIntentDefault); 18     } else if ([response.MIMEType isEqualToString:@"image/jpeg"]) { 19         imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault); 20  21         if (imageRef) { 22             CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef); 23             CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace); 24  25             // CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so fall back to AFImageWithDataAtScale 26             if (imageColorSpaceModel == kCGColorSpaceModelCMYK) { 27                 CGImageRelease(imageRef); 28                 imageRef = NULL; 29             } 30         } 31     } 32  33     CGDataProviderRelease(dataProvider); 34  35     UIImage *image = AFImageWithDataAtScale(data, scale); 36     if (!imageRef) { 37         if (image.images || !image) { 38             return image; 39         } 40  41         imageRef = CGImageCreateCopy([image CGImage]); 42         if (!imageRef) { 43             return nil; 44         } 45     } 46      47     // 代码到了这里,这个imageRef肯定有值了,有可能是response.MIMEType得到的,也有可能是根据image得到的 48  49     size_t width = CGImageGetWidth(imageRef); 50     size_t height = CGImageGetHeight(imageRef); 51     size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef); 52  53     // 这行代码不太明白什么意思。。 54     if (width * height > 1024 * 1024 || bitsPerComponent > 8) { 55         CGImageRelease(imageRef); 56  57         return image; 58     } 59  60     // CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate 61     size_t bytesPerRow = 0; 62     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 63     CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace); 64     CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); 65  66     if (colorSpaceModel == kCGColorSpaceModelRGB) { 67         uint32_t alpha = (bitmapInfo & kCGBitmapAlphaInfoMask); 68 #pragma clang diagnostic push 69 #pragma clang diagnostic ignored "-Wassign-enum" 70         if (alpha == kCGImageAlphaNone) { 71             bitmapInfo &= ~kCGBitmapAlphaInfoMask; 72             bitmapInfo |= kCGImageAlphaNoneSkipFirst; 73         } else if (!(alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast)) { 74             bitmapInfo &= ~kCGBitmapAlphaInfoMask; 75             bitmapInfo |= kCGImageAlphaPremultipliedFirst; 76         } 77 #pragma clang diagnostic pop 78     } 79  80     CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo); 81  82     CGColorSpaceRelease(colorSpace); 83  84     if (!context) { 85         CGImageRelease(imageRef); 86  87         return image; 88     } 89  90     CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), imageRef); 91     CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context); 92  93     CGContextRelease(context); 94  95     UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation]; 96  97     CGImageRelease(inflatedImageRef); 98     CGImageRelease(imageRef); 99 100     return inflatedImage;101 }

上边的这个方法设计到了CoreGraphics的知识,这个还不是特别了解。稍后会补一补这方面的知识。

上边的代码有一点不明白的地方,:

1   // 这行代码不太明白什么意思。。2     if (width * height > 1024 * 1024 || bitsPerComponent > 8) {3         CGImageRelease(imageRef);4 5         return image;6     }

 

后来看到了一篇文章 把原话粘贴出来

AFJSONResponseSerializer使用系统内置的NSJSONSerialization解析json,NSJSON只支持解析UTF8编码的数据(还有UTF-16LE之类的,都不常用),所以要先把返回的数据转成UTF8格式。这里会尝试用HTTP返回的编码类型和自己设置的stringEncoding去把数据解码转成字符串NSString,再把NSString用UTF8编码转成NSData,再用NSJSONSerialization解析成对象返回。上述过程是NSData->NSString->NSData->NSObject,这里有个问题,如果你能确定服务端返回的是UTF8编码的json数据,那NSData->NSString->NSData这两步就是无意义的,而且这两步进行了两次编解码,很浪费性能,所以如果确定服务端返回utf8编码数据,就建议自己再写个JSONResponseSerializer,跳过这两个步骤。此外AFJSONResponseSerializer专门写了个方法去除NSNull,直接把对象里值是NSNull的键去掉,还蛮贴心,若不去掉,上层很容易忽略了这个数据类型,判断了数据是否nil没判断是否NSNull,进行了错误的调用导致core。图片解压当我们调用UIImage的方法imageWithData:方法把数据转成UIImage对象后,其实这时UIImage对象还没准备好需要渲染到屏幕的数据,现在的网络图像PNG和JPG都是压缩格式,需要把它们解压转成bitmap后才能渲染到屏幕上,如果不做任何处理,当你把UIImage赋给UIImageView,在渲染之前底层会判断到UIImage对象未解压,没有bitmap数据,这时会在主线程对图片进行解压操作,再渲染到屏幕上。这个解压操作是比较耗时的,如果任由它在主线程做,可能会导致速度慢UI卡顿的问题。AFImageResponseSerializer除了把返回数据解析成UIImage外,还会把图像数据解压,这个处理是在子线程(AFNetworking专用的一条线程,详见AFURLConnectionOperation),处理后上层使用返回的UIImage在主线程渲染时就不需要做解压这步操作,主线程减轻了负担,减少了UI卡顿问题。具体实现上在AFInflatedImageFromResponseWithDataAtScale里,创建一个画布,把UIImage画在画布上,再把这个画布保存成UIImage返回给上层。只有JPG和PNG才会尝试去做解压操作,期间如果解压失败,或者遇到CMKY颜色格式的jpg,或者图像太大(解压后的bitmap太占内存,一个像素3-4字节,搞不好内存就爆掉了),就直接返回未解压的图像。另外在代码里看到iOS才需要这样手动解压,MacOS上已经有封装好的对象NSBitmapImageRep可以做这个事。关于图片解压,还有几个问题不清楚:1.本来以为调用imageWithData方法只是持有了数据,没有做解压相关的事,后来看到调用堆栈发现已经做了一些解压操作,从调用名字看进行了huffman解码,不知还会继续做到解码jpg的哪一步。 UIImage_jpg2.以上图片手动解压方式都是在CPU进行的,如果不进行手动解压,把图片放进layer里,让底层自动做这个事,是会用GPU进行的解压的。不知用GPU解压与用CPU解压速度会差多少,如果GPU速度很快,就算是在主线程做解压,也变得可以接受了,就不需要手动解压这样的优化了,不过目前没找到方法检测GPU解压的速度。

原来做这个转化的目的是为了尽量避免在主线程解压数据,因图像太大造成内存崩溃的问题。

============================== 分割线 ==================================

 

支持的ContentType:

  • image/tiff

  • image/jpeg

  • image/gif

  • image/png

  • image/ico

  • image/x-icon

  • image/bmp

  • image/x-bmp

  • image/x-xbitmap

  • image/x-win-bitmap

 

===========================  分割线 ==============================

 

 

这个复合型的子类有一个数组属性,里边装着多种序列化类型。看其实现方法也是,遍历数组中的内容,只要能转化成功,就返回数据。

 

============================= 分割线 =================================

 

好了,这篇就到此为止了,我们学到了

  • 通过一个协议来得到不同转换的结果。
  • 知道了AFNetworking响应结果支持的各种类型。
  • 大体了解了NSIndexSet的使用方法
  • 如果创建一个NSError 和 带有优先错误的NSUnderlyingErrorKey
  • 服务器返回的图片是压缩格式,要进行解压
  • 使用images来实现gif效果

 

转载地址:http://jjcml.baihongyu.com/

你可能感兴趣的文章
REGEXP_EXTRACT
查看>>
购物网站常见的展示商品的div
查看>>
iOS开发~CocoaPods使用详细说明
查看>>
Confluence 6 使用 CSS 样式化 Confluence 的介绍
查看>>
Go 语言开源发布 9 周年!社区贡献指数创新高
查看>>
MySQL中的账号与权限管理
查看>>
Java原生实现定时器
查看>>
【我们一起写框架】MVVM的WPF框架(三)—数据控件
查看>>
Confluence 6 导入模板的定义
查看>>
过拟合和欠拟合
查看>>
《利用Python进行数据分析·第2版》第4章 NumPy基础:数组和矢量计算
查看>>
Android BaseDialog(开发必备)动画、加载进度、阴影
查看>>
Java高并发秒杀Api-业务分析与DAO层构建2
查看>>
Java内存模型-volatile内存语义
查看>>
如何在 Confluence 中访问已关联的其他应用
查看>>
Spring Boot项目Docker化快速上手
查看>>
Python全栈 Web(基于Flask框架的完整项目、项目搭建)
查看>>
老司机也该掌握的MySQL优化指南
查看>>
HttpClientUtil 工具
查看>>
MongoDB最简单的入门教程之一 环境搭建
查看>>