Skip to content

Latest commit

 

History

History
162 lines (99 loc) · 12.5 KB

08.md

File metadata and controls

162 lines (99 loc) · 12.5 KB

2018.08

区分SDWebImage的三种缓存

作者: 陈满iOS

SDWebImage的三种缓存分为:内存图片缓存,磁盘图片缓存,内存操作缓存,步骤如下

  • 先查看内存图片缓存,内存图片缓存没有,后生成操作,查看磁盘图片缓存
  • 磁盘图片缓存有,就加载到内存缓存,没有就下载图片
  • 在建立下载操作之前,判断下载操作是否存在
  • 默认情况下,下载的图片数据会同时缓存到内存和磁盘中

流程如下图1所示。

图1

关于缓存位置

  • 内存缓存是通过 NSCache的子类AutoPurgeCache来实现的;
  • 磁盘缓存是通过 NSFileManager 来实现文件的存储(默认路径为/Library/Caches/default/com.hackemist.SDWebImageCache.default),是异步实现的。

关于图片下载操作

SDWebImage的大部分工作是由缓存对象SDImageCache和异步下载器管理对象SDWebImageManager来完成的。

SDWebImage的图片下载是由SDWebImageDownloader这个类来实现的,它是一个异步下载管理器,下载过程中增加了对图片加载做了优化的处理。而真正实现图片下载的是自定义的一个Operation操作,将该操作加入到下载管理器的操作队列downloadQueue中,Operation操作依赖系统提供的NSURLConnection类实现图片的下载。

关于SDWebImageManager与SDWebImageDownloader的分工

  • SDWebImageManager提供的关键API是loadImageWithURL开头的,负责加载的,加载load这个词跟下载download不同,比它更广,加载负责管理下载之前的操作:

管理下载操作的开始和取消 下载之前查询图片的内存缓存和磁盘缓存 下载之后保存图片到内存缓存和磁盘缓存 返回一个操作对象给上级对象UIImageView+WebCache作为操作缓存数组属性中去

  • SDWebImageDownloader提供的关键API是downloadImageWithURL开头的,可见它仅仅管理下载的操作,没有缓存的管理功能。

再谈数组、集合、字典与 hash、isEqual 方法的关联

作者: halohily

我们或多或少了解,Objective-C 中的 NSArray、NSSet、NSDictionary 与 NSObject 及其子类对象的 hash、isEqual 方法有许多联系,这篇小集讲一下其中的一些细节。

NSArray 允许添加重复元素,添加元素时不查重,所以不调用上述两个方法。在移除元素时,会对当前数组内的元素进行遍历,每个元素的 isEqual 方法都会被调用(使用 remove 方法传入的元素作为参数),所有返回真值的元素都被移除。在字典中,不涉及 hash 方法。

NSSet 不允许添加重复元素,所以添加新元素时,该元素的 hash 方法会被调用。若集合中不存在与此元素 hash 值相同的元素,则它直接被加入集合,不调用 isEqual 方法;若存在,则调用集合内的对应元素的 isEqual 方法,返回真值则判等,不加入,处理结束。若返回 false,则判定集合内不存在该元素,将其加入。

从集合中移除元素时,首先调用它的 hash 方法。若集合中存在与其 hash 值相等的元素,则调用该元素的 isEqual 方法,若真值则判等,进行移除;若不存在,则会依次调用集合中每个元素的 isEqual 方法,只要找到一个返回真值的元素,就进行移除,并结束整个过程。(所以这样会有其他满足 isEqual 方法但却被漏掉未被移除的元素)。调用 contains 方法时,过程类似。

因此,**若某自定义对象会被加入到集合或作为字典的 key 时,需要同时重写 isEqual 方法和 hash 方法。**这样,若集合中某元素存在,则调用它的 contains 和 remove 方法时,可以在 O(1) 完成查询。否则,查询它的时间复杂度提升为 O(n)。

值得注意的是,NSDictionary 的键和值都是对象类型即可。但是被设为键的对象需要遵守 NSCopying 协议

关于IAP丢单的处理

作者: 高老师很忙

做IAP(In-App Purchase)功能都有可能遇到丢单的问题,丢单是用户已经付款,但是因为某种原因客户端没有办法处理后续的操作,比如说根本没有收到苹果支付成功的回调,或者在与服务器验证票据过程中断网等等。如果处理不好,很容易击溃用户的对产品的信用度。

苹果的推荐做法是合理使用transaction,在AppDelegate.m的application:didFinishLaunchingWithOptions:方法里添加[[SKPaymentQueue defaultQueue] addTransactionObserver:xxx];如果还没有调用[[SKPaymentQueue defaultQueue] finishTransaction:xxx],那么在你下次启动App的时候,就会在paymentQueue:updatedTransactions:回调里收到未完成的transaction,然后继续进行处理,所以需要你在合适的时机去调用finishTransaction方法,比如说整个支付流程已经完成(包括已经成功验证票据)的时候,这个可以根据你的业务情况来确定调用时机,这样可以大大降低丢单的概率。不要忘了removeTransactionObserver哦!

如果觉得处理丢单的时间有点久,可以根据实际情况把相关信息存到本地(如果后续处理流程有需要业务信息的,这种情况是必须要存本地的),在切换到有网或者切换用户或者你觉得合适的实际去处理后续流程,这样也可以双保险,可以把损失降到最低。

如果有更好的方案,欢迎一起分享讨论。

使用 Keychain 存储登录态需要注意的一个坑

作者: KANGZUBIN

今天要讨论的这个问题你可能永远都不会遇到,而且绝大部分情况下你很难在开发中事先预料到它未来可能会发生,但是一旦不幸发生了,可能就是一个很严重的线上问题,惨痛教训。

我们通常会在 Keychain(钥匙串)中存储一些密码、用户登录态等敏感数据,一是可以提高保存数据的安全性;二是当用户卸载 App 后重新安装,可以自动登录保留上次的登录态;三是同一开发者账号下的不同 App,如果是采用同一套账户体系,就可以通过 Keychain Groups 共享登录态。

我们的 App 之前都是只把用户的登录态保存在 Keychain 中,并在 App 启动时去读取它,这一直也都没什么问题。前一段时间我们的 App 由于业务合规的原因审核被拒,按照苹果的要求不得不把 App 从公司的 A 开发者账号转让到 B 开发者账号下(公司旗下有很多不同主体的开发者账号),转让过程很顺利,但发版后短时间内收到大面积的用户反馈说,更新新版本后提示“登录失效,需要重新登录”。

原因很容易就可以猜到,App 从 A 转让到 B,就无法读取保存在 A 账号下的 Keychain 数据了,用户更新版本覆盖安装后,打开 App 也就无法获取之前的登录态了。

而且对于这种已经发生的问题,我们似乎也没有什么有效的补救措施,临时加急再发一版似乎也解决不了问题,因为之前的 Keychain 数据就是读取不到了,总不能再把 App 转让回去吧,😂

那么如何未雨绸缪预防以后再发生这种因为转让 App 导致存储在 Keychain 中的登录态丢失读取不到呢?(虽然出现转让 App 的概率非常低)

我们在新版本中采用了一种兼容的方法:把用户的登录态同时加密存储在本地缓存(Sandbox)和 Keychain 中,在 App 启动时,优先从 Keychain 中读取,如果 Keychain 中取不到,就从本地缓存中取(然后再把本地缓存的同步到 Keychain 中,因为即使 App 转让了,用户更新版本覆盖安装后 Sandbox 中的数据是不会变的),如果两处都取不到,就认为未登录。

你有没有更好的解决方案?欢迎留言讨论。

另外,有很多人通过 Keychain 来存储设备唯一标示符,也需要注意这个问题。

关于 Keychain 如何使用,可以参考苹果官方文档:GenericKeychain,而关于 Keychain 滥用问题的讨论,可以看 V2EX 的这个帖子

给 UIView 添加阴影

作者: Lefe_x

给 UIView 添加阴影看似简单,如果操作不当也可能会浪费你一些时间。有时候明明添加了阴影可是在 UI 上却没显示出来,尤其涉及到 cell 复用的情况。这里总结几条阴影不显示的原因:

  • 是否设置了 masksToBounds 为 YES,设置为 masksToBounds=YES,阴影不显示;
  • 设置阴影时 view 的 frame 是否为 CGRectZero,如果是,即使设置阴影后修改 frame 不为 CGRectZero 时,也不会显示阴影;
  • 使用自动布局时往往会遇到 frame 为 CGRectZero 时设置阴影无效,这时可以使用 layoutIfNeeded 方法;

通过 layer 设置阴影

// 阴影的颜色
self.imageView.layer.shadowColor = [UIColor blackColor].CGColor;
self.imageView.layer.shadowOpacity = 0.8;
// 阴影的圆角
self.imageView.layer.shadowRadius = 1;
// 阴影偏离的位置 (100, 50) x 方向偏离 100,y 偏离 50 正向,如果是负数正好为相反的方向
self.imageView.layer.shadowOffset = CGSizeMake(3, 4);

通过 shadowPath 设置阴影

通过这种方式设置的阴影可以自定义阴影的形状,它会使用在 layer 上设置的属性,比如 shadowRadius。

UIEdgeInsets edges = UIEdgeInsetsMake(15, 10, 15, 10);
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(-edges.left, -edges.top, CGRectGetWidth(self.imageView.frame) + edges.left + edges.right, CGRectGetHeight(self.imageView.frame) + edges.top + edges.bottom)];
self.imageView.layer.shadowPath = path.CGPath;

比较三种网络框架上传图片过程中的不同点?

作者: 陈满iOS

AFNetworking上传图片的步骤是利用图片设置到request的HTTPBodyStream中去,然后利用带有图片的request新建task上传。HYNetworking内部实现上传图片的时候,其实就是采用AFNetworking关于上传图片的API,都是AFNetworking里面一个API。XMNetworking上传图片请求也是基于AFNetworking上传进行的封装,不过比HYNetworking更加隐晦而已,另外它封装了上次图片数组的方法。

AFNetworking

  1. 压缩转换:UIImage实例对象通过UIImageJPEGRepresentation(压缩)转换为NSData,下面称之为imageData。
  2. 信息整合:将imageData与文件名fileName,文件路径name,类型名mimeType整合成图片模型(AFHTTPBodyPart)的一个对象bodyPart中去。
  3. 添加图片模型:将上面新建好的图片模型对象bodyPart,向图片输入流(AFMultipartBodyStream)的对象bodyStream的数组属性(HTTPBodyParts)添加。
  4. 设置request的HTTPBodyStream属性为bodyStream:封装为requestByFinalizingMultipartFormData
  5. 将图片模型对象formData用AFNetwork的POST请求与uploadTaskWithStreamedRequest方法进行上传。

HYBNetworking

  1. 压缩转换:UIImage实例对象通过UIImageJPEGRepresentation压缩转换为NSData,下面称之为imageData。
  2. 信息整合:利用AFNetwork的appendPartWithFileData,将imageData与文件名fileName,文件路径name,类型名mimeType整合成图片模型(AFStreamingMultipartFormData)的一个对象formData中去。
  3. 将图片模型对象formData用AFNetwork的POST请求与uploadTaskWithStreamedRequest方法进行上传。

XMNetworking

  1. 压缩转换:UIImage实例对象通过UIImageJPEGRepresentation压缩转换为NSData,下面称之为imageData。
  2. 信息整合:利用AFNetwork的appendPartWithFileData,将imageData与文件名fileName,文件路径name,类型名mimeType整合成图片模型(XMUploadFormData)的一个对象formData中去。
  3. 添加图片模型:向管理器的图片模型数组uploadFormDatas添加上面新建好的图片模型对象formData。
  4. 遍历图片模型数组,获得图片模型,利用AFNetwork的POST请求与uploadTaskWithStreamedRequest方法进行上传。

【总结】 可见,上面三种框架都是基于AFNetworking进行的封装,实质的流程还是一样的。上传图片的流程图1所示。

图1