上周公众号发布的如下文章:html
本期知识小集的主要内容包括:ios
做者: 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 协议。服务器
做者: 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 的这个帖子。
做者: Lefe_x
给 UIView 添加阴影看似简单,若是操做不当也可能会浪费你一些时间。有时候明明添加了阴影但是在 UI 上却没显示出来,尤为涉及到 cell 复用的状况。这里总结几条阴影不显示的缘由:
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 进行的封装,实质的流程仍是同样的。上传图片的流程图以下所示。
在项目中,有时候会遇到某些个页面须要隐藏导航栏,通常状况下,咱们会在 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(In-App Purchase)功能都有可能遇到丢单的问题,丢单是用户已经付款,可是由于某种缘由客户端没有办法处理后续的操做,好比说根本没有收到苹果支付成功的回调,或者在与服务器验证票据过程当中断网等等。若是处理很差,很容易击溃用户的对产品的信用度。
苹果的推荐作法是合理使用 transaction,在 AppDelegate.m 的 application:didFinishLaunchingWithOptions:
方法里添加 [[SKPaymentQueue defaultQueue] addTransactionObserver:xxx]
;若是尚未调用 [[SKPaymentQueue defaultQueue] finishTransaction:xxx]
,那么在你下次启动 App 的时候,就会在 paymentQueue:updatedTransactions:
回调里收到未完成的 transaction,而后继续进行处理,因此须要你在合适的时机去调用 finishTransaction 方法,好比说整个支付流程已经完成(包括已经成功验证票据)的时候,这个能够根据你的业务状况来肯定调用时机,这样能够大大下降丢单的几率。不要忘了 removeTransactionObserver 哦!
若是以为处理丢单的时间有点久,能够根据实际状况把相关信息存到本地(若是后续处理流程有须要业务信息的,这种状况是必需要存本地的),在切换到有网或者切换用户或者你以为合适的实际去处理后续流程,这样也能够双保险,能够把损失降到最低。
欢迎关注咱们的公众号:iOS-Tips,也欢迎加入咱们的群组讨论问题。能够公众号留言 ios
、flutter
、web
、pwa
、小程序
等关键词获取入群方式。