「 iOS 知识小集 」2018 · 第 24 期

上周公众号发布的如下文章:html

本期知识小集的主要内容包括:ios

  • 再谈数组、集合、字典与 hash、isEqual 方法的关联
  • 使用 Keychain 存储登陆态须要注意的一个坑
  • 给 UIView 添加阴影
  • 比较三种网络框架上传图片过程当中的不一样点?
  • 经过 runtime 控制导航栏的 hidden 属性
  • 关于 IAP 丢单的处理

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

做者: halohilyweb

咱们或多或少了解,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 协议。服务器

使用 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 进行的封装,实质的流程仍是同样的。上传图片的流程图以下所示。

经过 runtime 控制导航栏的 hidden 属性

在项目中,有时候会遇到某些个页面须要隐藏导航栏,通常状况下,咱们会在 viewWillAppear 和 viewDidDisappear 去设置 navigationBar 的 hidden 属性,如图一。

这里介绍另外一种方法,经过 runtime 去控制。

创建 vc 的 category,声明 Bool 类型的 hideNavigationBar 属性。主要思路是:重写 initialize 方法,而且自定义一个方法经过 runtime 去替代系统的 viewWillAppear,在该自定义方法里,就是去设置 navigationBar 的 hidden 属性。具体代码如图二。

代码中用到的几个 runtime 方法简单说明下:

//获取类中的方法实现
    class_getMethodImplementation
//替换Method
    method_exchangeImplementations
//获取类中的某个实例方法
    class_getInstanceMethod
//关联对象,至关于 setValue:forKey 
    objc_setAssociatedObject
复制代码

关于使用方法,因为 hideNavigationBar 默认是 NO,因此只在须要隐藏导航栏的页面调用便可,代码如图三。

关于 IAP 丢单的处理

做者: 高老师很忙

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

苹果的推荐作法是合理使用 transaction,在 AppDelegate.m 的 application:didFinishLaunchingWithOptions: 方法里添加 [[SKPaymentQueue defaultQueue] addTransactionObserver:xxx] ;若是尚未调用 [[SKPaymentQueue defaultQueue] finishTransaction:xxx],那么在你下次启动 App 的时候,就会在 paymentQueue:updatedTransactions: 回调里收到未完成的 transaction,而后继续进行处理,因此须要你在合适的时机去调用 finishTransaction 方法,好比说整个支付流程已经完成(包括已经成功验证票据)的时候,这个能够根据你的业务状况来肯定调用时机,这样能够大大下降丢单的几率。不要忘了 removeTransactionObserver 哦!

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

关注咱们

欢迎关注咱们的公众号:iOS-Tips,也欢迎加入咱们的群组讨论问题。能够公众号留言 iosflutterwebpwa小程序 等关键词获取入群方式。

相关文章
相关标签/搜索