解决CocoaAsyncSocket在iOS16系统上的崩溃问题

·  阅读 7547
解决CocoaAsyncSocket在iOS16系统上的崩溃问题

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章,点击查看活动详情

本篇文章记录了从昨天更新iOS16发现的一个崩溃问题到解决的过程。

前言

昨天中秋节后第一天上班,苹果爸爸推送了iOS16系统。

于是,作为iOS开发者的常规操作,我开始漫长升级之路:

  • 升级macOS到12.6
  • 升级iOS16
  • 升级Xcode14

运行App,然后崩溃了,崩溃的截图信息如下:

image.png

简单复现崩溃情况

因为Xcode14是后续持续开发刚需的IDE版本(理论上说目前使用Xcode13也可以继续进行开发,但是对于适配iOS16,还是Xcode14更好),我们对崩溃问题进行了简单的复现统计:

IDE版本iOS版本设备是否崩溃
Xcode14iOS 15真机
Xcode14iOS 15模拟器
Xcode14iOS 16真机
Xcode14iOS 16模拟器

可以看出,该崩溃仅仅只在iOS16的真机上出现。

排查定位问题

根据信息,我们可以发现崩溃的问题出现在CocoaMQTT相关的依赖库——CocoaAsyncSocket

于是我们先去CocoaMQTT上面去看了一些open issue:

image.png

有一个8月15日提交的反馈的issue,大概是说在Xcode14的beta5上有崩溃,而且也是涉及CocoaAsyncSocket

于是乎,我们顺藤摸瓜继续去依赖库CocoaAsyncSocket上面去找找。

果然,一个issue非常醒目:

image.png

I think this is a problem inside iOS 16 Core Foundation framework. The source code of new Core Foundation is not released, so I just file a bug to apple(FB11489606).

根据反馈者的意见:认为这个bug可能是由于iOS16架包中的Core Foundation framework导致。

于是我们又顺带看了看CocoaAsyncSocket的PR:

image.png

第一个PR就格外醒目!解决iOS16在后台的崩溃问题。

image.png

虽然这个PR还没有合并,但是对于我们App开始连接MQTT就崩溃的情况还是值得试一试的,于是我们立即在Pod的源码中对这里进行了修改。

修改后,MQTT正常工作,也没有崩溃了。

难道你觉得到这里已经完了?并没有,我们接着往下看。

深入:kCFStreamNetworkServiceTypeVoIP过期导致的崩溃

我特地去看了一下有关kCFStreamNetworkServiceTypeVoIP的代码,其介绍如下:

/* deprecated network service type: */

CFN_EXPORT const CFStringRef kCFStreamNetworkServiceTypeVoIP       CF_DEPRECATED(10_7, 10_11, 4_0, 9_0, "use PushKit for VoIP control purposes");   // voice over IP control - this service type is deprecated in favor of using PushKit for VoIP control
复制代码

kCFStreamNetworkServiceTypeVoIP这个常量实际上早在iOS9就已经过期了。

甚至2016年,在CocoaAsyncSocket中Close的issue中就有反馈这个问题:

Snip20220914_8.png

但是,在最新的2020年12月14日的CocoaAsyncSocket7.6.5版本中依旧还是这么写的:

Snip20220914_9.png

既然kCFStreamNetworkServiceTypeVoIP已经过期了,那么我就用issuse 402里面提到的PKPushTypeVoIP替换一下试试。

image.png

编译,运行,App没有崩溃!!!

将过期的kCFStreamNetworkServiceTypeVoIP改为使用PKPushTypeVoIP才是解决问题的关键!!!

复盘CocoaAsyncSocket的崩溃过程(2022年9月16日更新)

更新完这篇文章后,我也收到了一些掘友的反馈,说他们的CocoaAsyncSocket并没有出现崩溃情况,再加上我最近也一直到Github上看有关的问题,于是整理复盘一下。

首先,我要说的是,我出现CocoaAsyncSocket的崩溃,是因为我使用的CocoaMQTT导致的。

下面是截取的崩溃定位的代码位置,详细情况可以看这里:Crash when build by Xcode 14.0 beta 5 (14A5294e) and run on iOS 16.0 (20A5339d)

image.png

而CocoaMQTTSocke的这个backgroundOnSocket默认属性上是true:

image.png

由于我在自己项目里,并没有对backgroundOnSocket进行更改,于是就调用了CocoaAsyncSocket中的enableBackgroundingOnSocket()方法。

我们接着继续搜索enableBackgroundingOnSocket()方法,于是可以看到它本质调用的是这个- (BOOL)enableBackgroundingOnSocketWithCaveat:(BOOL)caveat方法:

image.png

最后我们找到- (BOOL)enableBackgroundingOnSocketWithCaveat:(BOOL)caveat针对kCFStreamNetworkServiceTypeVoIP改为PKPushTypeVoIP的配置,真相也就大白了:

image.png

最后也就是说主动调用CocoaAsyncSocket中的对外API- (BOOL)enableBackgroundingOnSocket;才会出现崩溃:

image.png

这个bug已经埋藏久矣

这两天稍微关注了一下CocoaAsyncSocket的PR与issues,真的是感觉有点哭笑不得,因为很久很久以前就有大佬提交了相关PR及其Bug的严重性,甚至苦口婆心的说要赶紧更新,不然死一大片,然而官方就是没有什么动静:

image.png

感觉也是挺悲哀的,人家关闭这个issue的理由我也很震惊:

image.png

PushKit framework不支持全平台,所以还是用过期的API吧。而这一切的讨论是在2015-2016年发生的。

一直过期一直爽,爽到崩溃就好了。难道就不能多些一点#if区分平台来解决吗?

Version 7.6.5 will CRASH on iOS 16 due to removed kCFStreamNetworkServiceTypeVoIP #801

总结

在本篇,我们解决了CocoaAsyncSocket在iOS16系统上的崩溃问题,其实没有太多技巧而言。

我们首先通过Xcode崩溃的信息,基本定位到了CocoaAsyncSocket,然后在通过Github中的issuesPR,了解到了相关API的替换,最后发现kCFStreamNetworkServiceTypeVoIP已经过期了,使用之前已经有大佬提出的方案,就解决了这个问题。

还记得我们之前简单统计吗?崩溃只在iOS16的真机出现,而且有开发者认为是iOS16 SDK的Bug导致

而我通过替换kCFStreamNetworkServiceTypeVoIP改为PKPushTypeVoIP解决这个问题后,我更倾向于这个观点:

在iOS16 SDK中,可能kCFStreamNetworkServiceTypeVoIP真的失效了,没有意义了,所以继续使用kCFStreamNetworkServiceTypeVoIP并不能完成配置,所以导致了崩溃。

到这篇文章发布之前,我已经PR了代码到CocoaAsyncSocket,至于会不会被采纳,那就不知道了。

image.png

考虑到涉及使用CocoaAsyncSocket的App与第三库众多,也希望官方大佬早点解决这个问题吧。

参考文档

使用 CocoaAsyncSocket “kCFStreamNetworkServiceTypeVoIP is deprecated in iOS 9 ” warning 解决方案

kCFStreamNetworkServiceTypeVoIP is deprecated in iOS 9 warning

fix crash of backgrouding in iOS16

自己写的项目,欢迎大家star⭐️

RxStudy:RxSwift/RxCocoa框架,MVVM模式编写wanandroid客户端。

GetXStudy:使用GetX,重构了Flutter wanandroid客户端。

分类:
iOS
收藏成功!
已添加到「」, 点击更改