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

1,952 阅读6分钟

原文链接

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

  • Mac 命令行效率提升利器篇
  • YYCache 源码解析(一):使用方法,架构与内存缓存的设计
  • 深入浅出 iOS 并发编程
  • 单一职责原则在 iOS 中的应用

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

  • NSURLProtocol 的实际使用踩坑总结
  • WKWebView 给 scrollView 添加 delegate crash
  • 不需要接入 SDK 的三方登录及分享
  • instancetypeid 的区别

NSURLProtocol 的实际使用踩坑总结

使用 NSURLProtocol 拦截网络请求”相信很多人都有所耳闻,最近出于工作需要比较详细地研究了一番,下面给出一些比较有用的踩坑总结。

1、需要明确:无论你有没有用 NSURLProtocol 对 app 中的所有网络请求做统一处理,这些网络请求的发起者都应该是无感知的。举个例子,在某个业务场景下,我使用自定义的 URLSession 发起网络请求,并实现各个代理方法来处理网络请求各个阶段的事件。如果这个网络请求被 NSURLProtocol 拦截,仍然需要保证当初实现的各个代理方法都能被如期调用。为此,苹果定义了 NSURLProtocolClient 协议,协议方法覆盖了网络请求完整的生命周期。只要你在拦截之后重发的请求的各阶段适时、完整地调用了协议中的方法,那么最初的请求发起者便对“拦截”这件事毫无感知了,它的代理事件或者回调的 block 都会在正确的时间被执行,无论这个请求最初是个 NSURLSessionDownloadTask 还是 NSURLConnection 。从这个角度来说,NSURLProtocol 是一个底层的拦截。

2、很多介绍 NSURLProtocol 的文章,会在每个 startLoading 方法的实现中,都创建 NSURLSession 实例,这是非常低效、失当的做法,具体原因其实和 NSURLProtocol 无关了,这是 NSURLSession 使用失当的问题。正确的实现应该是模拟 AFNetworking 的 SessionManager,共享一个 session 实例,却可以将归属于每一个 task 的回调事件交由每一个 NSURLProtocol 实例处理,具体可以参考官方给出的 NSURLProtocol demo。或者,甚至可以直接使用 AFNetworking 来对拦截后的请求进行重发。

3、拦截重发一个 Request 之后,记得使用 setProperty:forKey:inRequest: 方法对该 Request 进行标记,防止进入死循环。

4、通过配置 Configuration 自定义的 Session 发出的请求,默认是无法被拦截到的,因为 NSURLSessionConfiguration 的属性 protocolClasses 里面,默认不包含我们自定义的 NSURLProtocol。这时,可以 hook protocolClasses 方法,加入我们自己的 protocol。

5、有的同学可能会纠结,最初的网络请求如果是一个 NSURLSessionDownloadTask ,它需要处理带有已写入数据、完整数据等信息的回调方法,而在 NSURLProtocol 中被拦截之后,统一采用 NSURLSessionDataTask 来重发,那当初的下载独有的代理方法,还能被正确执行吗?答案是肯定的,原因参考第一条。其实通过 NSURLProtocol 只提供了 NSURLRequest 来初始化的方法就可以看出来,在 NSURLProtocol 中,我们不需要关注最初的任务是何种 task,因为无论最初是什么类型的 task,到 protocol 的初始化这里时,你已经无法获知它最初的 task 类型。

WKWebView 给 scrollView 添加 delegate crash

作者: Lefe_x

想监听 WKWebView 的滚动,我的做法是设置 WKWebView.scrollView.delegate,然而这种方法会导致在 iOS9 上 crash。使用全局断点并不能定位到 crash 的具体位置,当 crash 后,在打印控制台处输入 bt,发现有输出异常信息,大体意思是 WKWebView 已经释放,但在其它地方还在使用它的属性 scrollView。

关于这个 crash 的描述:

Possible crash when setting the WKWebViews's scroll view delegate, if the scroll view outlives the web view

Null out the internal delegate on the WKScrollView when the WKWebView goes away, since it's possible for a client to set its own scroll view delegate, forcing the creation of a WKScrollViewDelegateForwarder, and then retain the UIScrollView past the lifetime of the WKWebView. In this situation, the WKScrollViewDelegateForwarder's internalDelegate would point to a deleted WKWebView.

想解决这个问题需要在,dealloc 的位置把 delegate 设置为 nil:

self.webView.scrollView.delegate = nil;

参考:

不需要接入 SDK 的三方登录及分享

**作者:**这个汤圆没有馅

提到三方登录分享,第一反应大概是友盟、ShareSDK 之类。集成微信、QQ、微博三个平台,友盟 SDK 大小 62.9M,ShareSDK 大小 74M,如果直接集成三个平台的官方 SDK,分别大小如图,则一共有 51M。

接入 SDK 后,在授权登录的回调里拿到各个平台需要的值,再丢给后台,由后台去请求用户信息再与自己的用户体系绑定。(当然每个人的方案可能不一样,这只是其中一种) 如图下图,微信需要 code ,QQ 需要 openId 和 accessToken,微博需要 access_token。

这边介绍一个叫 MonkeyKing(github.com/nixzhu/Monk…) 的 Swift 库,文件大小只有121KB,通过直接访问url的方法拿到回调。

先调用 MonkeyKing.registerAccount 注册各个平台,再如图三调用授权登录的方法。

看了一下源码,实现原理如下:

  • 微信:通过 weixin://app/YourAppId/auth/?scope=snsapi_userinfo&state=Weixinauth 拿到 code 值;
  • QQ:通过 mqqOpensdkSSoLogin://SSoLogin/tencentXXX/com.tencent.tencentXXX?generalpastboard=1 拿到 openId 和 accessToken;
  • 微博:是先设置剪切板参数,再通过 weibosdk://request?id=uuidString&sdkversion=003203000 获取access_token;

MonkeyKing 除了登录功能外,还有分享及支付功能等。而这些功能的实现不需要集成任何 SDK,对 App 体积大小有要求的人来说,这个库相当棒。

instancetypeid 的区别

作者: Vong_HUST

日常开发中,我们通常会复写各种指定构造器或者自定义指定构造器,一般返回值都会写成 instancetype,但是为什么要写 instancetype 而不是 id 呢?他们之间的区别在哪呢?

我们先来看下代码

@interface TestObjectA : NSObject
  
+ (id)createObjectA;
- (void)methodA;

@end
  
@interface TestObjectB : NSObject
  
+ (instancetype)createObjectB;
- (void)methodB;

@end
  
// 假设上面4个方法都有实现

[[TestObjectA createObjectA] methodB];      // 无编译错误,但是崩溃
[[TestObjectB createObjectB] methodA];      // 编译报错:No visible @interface for 'TestObjectB' declares the selector 'methodA'

从上图可以看出,区别就在于:instancetype 能够做到类型检测而 id 不行。前者仅可做方法返回值,不能作为参数。但是可能会有人会有疑问,为什么 - (id)initWithxxx: 也可以做到类型检测呢?因为类方法只要以 allocnew 开头就会有关联返回类型(即类型检测),实例方法只要以 initautoreleaseretainself 开头就会有关联返回类型。具体可以参考这篇文章

但是 ARC 下实测,实例方法只有 init 开头的才有关联返回类型。

关注我们

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

目前 iOS 3号群 已到 300+,期待你的加入。