SDWebimage - 核心逻辑
一、SDWebImage加载图片流程
SDWebImageClassDiagram.png SDWebImageSequenceDiagram.png- 1.先判断图片实在本地还是网络
- 2.判断当前有没有任务正在进行,(一个UIView有且只有一个任务能在执行,用一个全局的NSMapTable存储任务当前UI控件的任务)
- 3.SDWebImageManager调用loadImageWithURL
- 4.加载完成之后,当前这个图片进行处理(在UI界面进行显示,暴露一些api让用户可以自定义,也就是意味着当前这个图片是否需要护理,如果直接显示,需要调用setNeedsLayout,当runloop进入到下一个周期当中,拿到这个标记,就直接刷新了)
SDWebImage缓存模块
- 实现了内存和磁盘双缓存
1.内存缓存
config 压缩图片会提供更好的性能,但是图片特别大的时候,会造成内存暴增,如果出现,我们可以对这张高清图进行特别的设置(不进行压缩)
- SDMemoryCache: NSCache
NSCache 我们不能控制什么时候清理缓存,不归我们管,
而在SDMemoryCache使用NSMapTable存储当前的内存缓存,为什么不用NSDictionary(内存语义)呢,因为NSMapTable拥有更多的内存语义,当前存储的value是弱引用的,当会自动删除,当前存储的value是弱引用,不会对我们之外的对象产生任何影响,存放在全局的weak表。当对象被清除的时候,会自动删除key:value,监听了内存清除。
setObjforkey 调用super setvalue, 有一个开关shouldUseWeakMemoryCache,打开的话会存两份,存到我们自己的MapTable,是以空间换时间,可以自己控制
2.磁盘缓存
- 创建一个目录
- 为每一个缓存文件生成一个MD5文件名
先查找内存缓存,再在磁盘中查找,初始化一个NSOperation, 查找文件名,查到二进制,然后归档转为UIImage,同时根据设置缓存到内存缓存,加入了一个autoreleasepool 为了及时释放,
3.下载模块
- NSURLSession
- SDWebImageDownloader
1.配置下载相关参数
2.下载队列的先后顺序,先进先出,先进后出
- SDWebImageDownloader
- 最大任务数量
- HTTPS
初始化SDWebImageDownloaderOperation ,重写了一个start方法,下载完成之后,把当前的代理方法走到了SDWebImageDownloader的代理方法
4.NSOperation
5.NSURLSession 的代理方法
二、AFNetWorking核心流程
image.png image.png image.png1 NSUrlSessionManager
2 序列化,响应和请求的序列化
HTTPS流程
HTTPS是用来防止中间人攻击
RSA非对称加密的流程 (耗时用来验证)
对于客户端拿公钥对数据进行加密,给服务器,拿私钥进行解密
通过TCP发送
客户端拿公钥进行加密 ,
001.jpeg
1 .发送客户端SSL版本信息(包含随机数,支持的加密算法等等)通过TCP发送给服务端(服务器私钥)
2.服务器返回客户端SSL版本,随机数等信息及服务器公钥
RSA用于验证HTTPS
别人可以拿到公钥,却不能解密,
002.jpeg
那几个类 每个类的职责
AFHTTPSessionManager 子类负责主要的业务逻辑处理
AFURLSessionManager 核心类围绕他展开
序列化 分为请求和相应的序列化 参数拼接 多表单参数的提交
什么是序列化?
序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。
为什么要序列化对象
把对象转换为字节序列的过程称为对象的序列化
把字节序列恢复为对象的过程称为对象的反序列化
什么情况下需要序列化?
当我们需要把对象的状态信息通过网络进行传输,或者需要将对象的状态信息持久化,以便将来使用时都需要把对象进行序列化
image.png检测网络
a.png
安全验证
关于HTTPS比较好的文章
https://blog.csdn.net/xiaoming100001/article/details/81109617
关于http三次握手和四次挥手
https://baijiahao.baidu.com/s?id=1654225744653405133&wfr=spider&for=pc
其中比较重要的字段有:
(1)序号(sequence number):Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
(2)确认号(acknowledgement number):Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。
(3)标志位(Flags):共6个,即URG、ACK、PSH、RST、SYN、FIN等。具体含义如下:
URG:紧急指针(urgent pointer)有效。
ACK:确认序号有效。
PSH:接收方应该尽快将这个报文交给应用层。
RST:重置连接。
SYN:发起一个新连接。
计算图片大小
比对临界值
加减法操作
图片是有像素点组成,每一个像素点有四个向量RGBA
YYImage图片格式判断
图片格式判断
16进制
科普一下自节
“1位”表示为1bit
“1个字节”表示为1Byte
“1个字节”=“8位” 即1Bytes=8bit
因此“4个字节”=4*8位=32位
1字节 uint8_t;
2字节 uint16_t;
4字节 uint32_t;
8字节 uint64_t;
此外,字母用八位用1个字节(即8位)进行表示和存储,而一个汉字则使用两个字节(即16位)进行表示和存储
先判断后缀,然后前四到八个字节
YYImageType YYImageDetectType(CFDataRef data) {
if (!data) return YYImageTypeUnknown;
//拿到前八个字节的数据
uint64_t length = CFDataGetLength(data);
if (length < 16) return YYImageTypeUnknown;
const char *bytes = (char *)CFDataGetBytePtr(data);
uint32_t magic4 = *((uint32_t *)bytes);
switch (magic4) {
case YY_FOUR_CC(0x4D, 0x4D, 0x00, 0x2A): { // big endian TIFF
return YYImageTypeTIFF;
} break;
case YY_FOUR_CC(0x49, 0x49, 0x2A, 0x00): { // little endian TIFF
return YYImageTypeTIFF;
} break;
case YY_FOUR_CC(0x00, 0x00, 0x01, 0x00): { // ICO
return YYImageTypeICO;
} break;
case YY_FOUR_CC(0x00, 0x00, 0x02, 0x00): { // CUR
return YYImageTypeICO;
} break;
case YY_FOUR_CC('i', 'c', 'n', 's'): { // ICNS
return YYImageTypeICNS;
} break;
case YY_FOUR_CC('G', 'I', 'F', '8'): { // GIF
return YYImageTypeGIF;
} break;
case YY_FOUR_CC(0x89, 'P', 'N', 'G'): { // PNG
uint32_t tmp = *((uint32_t *)(bytes + 4));
if (tmp == YY_FOUR_CC('\r', '\n', 0x1A, '\n')) {
return YYImageTypePNG;
}
} break;
case YY_FOUR_CC('R', 'I', 'F', 'F'): { // WebP
uint32_t tmp = *((uint32_t *)(bytes + 8));
if (tmp == YY_FOUR_CC('W', 'E', 'B', 'P')) {
return YYImageTypeWebP;
}
} break;
/*
case YY_FOUR_CC('B', 'P', 'G', 0xFB): { // BPG
return YYImageTypeBPG;
} break;
*/
}
uint16_t magic2 = *((uint16_t *)bytes);
switch (magic2) {
case YY_TWO_CC('B', 'A'):
case YY_TWO_CC('B', 'M'):
case YY_TWO_CC('I', 'C'):
case YY_TWO_CC('P', 'I'):
case YY_TWO_CC('C', 'I'):
case YY_TWO_CC('C', 'P'): { // BMP
return YYImageTypeBMP;
}
case YY_TWO_CC(0xFF, 0x4F): { // JPEG2000
return YYImageTypeJPEG2000;
}
}
// JPG FF D8 FF
if (memcmp(bytes,"\377\330\377",3) == 0) return YYImageTypeJPEG;
// JP2
if (memcmp(bytes + 4, "\152\120\040\040\015", 5) == 0) return YYImageTypeJPEG2000;
return YYImageTypeUnknown;
}
- (instancetype)initWithImage:(UIImage *)image {
self = [super init];
_runloopMode = NSRunLoopCommonModes;
_autoPlayAnimatedImage = YES;
self.frame = (CGRect) {CGPointZero, image.size };
self.image = image;
return self;
}
- (void)startAnimating {
YYAnimatedImageType type = [self currentImageType];
if (type == YYAnimatedImageTypeImages || type == YYAnimatedImageTypeHighlightedImages) {
NSArray *images = [self imageForType:type];
if (images.count > 0) {
[super startAnimating];
self.currentIsPlayingAnimation = YES;
}
} else {
if (_curAnimatedImage && _link.paused) {
_curLoop = 0;
_loopEnd = NO;
_link.paused = NO;
self.currentIsPlayingAnimation = YES;
}
}
}
//重写了set方法
- (void)setImage:(id)image withType:(YYAnimatedImageType)type {
[self stopAnimating];
if (_link) [self resetAnimated];
_curFrame = nil;
switch (type) {
case YYAnimatedImageTypeNone: break;
case YYAnimatedImageTypeImage: super.image = image; break;
case YYAnimatedImageTypeHighlightedImage: super.highlightedImage = image; break;
case YYAnimatedImageTypeImages: super.animationImages = image; break;
case YYAnimatedImageTypeHighlightedImages: super.highlightedAnimationImages = image; break;
}
[self imageChanged];
}
- (void)imageChanged {
YYAnimatedImageType newType = [self currentImageType];
id newVisibleImage = [self imageForType:newType];
NSUInteger newImageFrameCount = 0;
BOOL hasContentsRect = NO;
if ([newVisibleImage isKindOfClass:[UIImage class]] &&
[newVisibleImage conformsToProtocol:@protocol(YYAnimatedImage)]) {
newImageFrameCount = ((UIImage<YYAnimatedImage> *) newVisibleImage).animatedImageFrameCount;
if (newImageFrameCount > 1) {
hasContentsRect = [((UIImage<YYAnimatedImage> *) newVisibleImage) respondsToSelector:@selector(animatedImageContentsRectAtIndex:)];
}
}
if (!hasContentsRect && _curImageHasContentsRect) {
if (!CGRectEqualToRect(self.layer.contentsRect, CGRectMake(0, 0, 1, 1)) ) {
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.layer.contentsRect = CGRectMake(0, 0, 1, 1);
[CATransaction commit];
}
}
_curImageHasContentsRect = hasContentsRect;
if (hasContentsRect) {
CGRect rect = [((UIImage<YYAnimatedImage> *) newVisibleImage) animatedImageContentsRectAtIndex:0];
[self setContentsRect:rect forImage:newVisibleImage];
}
if (newImageFrameCount > 1) {
[self resetAnimated];
_curAnimatedImage = newVisibleImage;
_curFrame = newVisibleImage;
_totalLoop = _curAnimatedImage.animatedImageLoopCount;
_totalFrameCount = _curAnimatedImage.animatedImageFrameCount;
[self calcMaxBufferCount];
}
[self setNeedsDisplay];
[self didMoved];
}
- (void)startAnimating {
YYAnimatedImageType type = [self currentImageType];
if (type == YYAnimatedImageTypeImages || type == YYAnimatedImageTypeHighlightedImages) {
NSArray *images = [self imageForType:type];
if (images.count > 0) {
[super startAnimating];
self.currentIsPlayingAnimation = YES;
}
} else {
if (_curAnimatedImage && _link.paused) {
_curLoop = 0;
_loopEnd = NO;
_link.paused = NO;
self.currentIsPlayingAnimation = YES;
}
}
}
- (void)step:(CADisplayLink *)link {
UIImage <YYAnimatedImage> *image = _curAnimatedImage;
NSMutableDictionary *buffer = _buffer;
UIImage *bufferedImage = nil;
NSUInteger nextIndex = (_curIndex + 1) % _totalFrameCount;
BOOL bufferIsFull = NO;
if (!image) return;
if (_loopEnd) { // view will keep in last frame
[self stopAnimating];
return;
}
NSTimeInterval delay = 0;
if (!_bufferMiss) {
_time += link.duration;
delay = [image animatedImageDurationAtIndex:_curIndex];
if (_time < delay) return;
_time -= delay;
if (nextIndex == 0) {
_curLoop++;
if (_curLoop >= _totalLoop && _totalLoop != 0) {
_loopEnd = YES;
[self stopAnimating];
[self.layer setNeedsDisplay]; // let system call `displayLayer:` before runloop sleep
return; // stop at last frame
}
}
delay = [image animatedImageDurationAtIndex:nextIndex];
if (_time > delay) _time = delay; // do not jump over frame
}
LOCK(
bufferedImage = buffer[@(nextIndex)];
if (bufferedImage) {
if ((int)_incrBufferCount < _totalFrameCount) {
[buffer removeObjectForKey:@(nextIndex)];
}
[self willChangeValueForKey:@"currentAnimatedImageIndex"];
_curIndex = nextIndex;
[self didChangeValueForKey:@"currentAnimatedImageIndex"];
_curFrame = bufferedImage == (id)[NSNull null] ? nil : bufferedImage;
if (_curImageHasContentsRect) {
_curContentsRect = [image animatedImageContentsRectAtIndex:_curIndex];
[self setContentsRect:_curContentsRect forImage:_curFrame];
}
nextIndex = (_curIndex + 1) % _totalFrameCount;
_bufferMiss = NO;
if (buffer.count == _totalFrameCount) {
bufferIsFull = YES;
}
} else {
_bufferMiss = YES;
}
)//LOCK
if (!_bufferMiss) {
[self.layer setNeedsDisplay]; // let system call `displayLayer:` before runloop sleep
}
if (!bufferIsFull && _requestQueue.operationCount == 0) { // if some work not finished, wait for next opportunity
_YYAnimatedImageViewFetchOperation *operation = [_YYAnimatedImageViewFetchOperation new];
operation.view = self;
operation.nextIndex = nextIndex;
operation.curImage = image;
[_requestQueue addOperation:operation];
}
}
timer进行比对
如果小于delay 说明动画没执行完,赋值让前面的先走完
大于delay 赋值 不跳过当前这一帧动画
YYImage加载动图原理
动图有一帧帧图片组成
用到了定时器CADisplayLink
framebuffer 存储的每一帧图片
LOOK 缓存中取关键帧