平时自己写App啥的有debugger在, 所以当app crash的时候就可以从Xcode啥的上直接看到问题出在哪.

然而像提交给Apple审查的App出问题的时候Apple是只会提供给你CrashReportLog的. 毕竟你给出的也只是可执行文件, Apple可不知道你里面代码长啥样.
所以如何解析CrashReport就是个技术活了, 好在macOS/iOS的CrashReport都挺好读的, 只不过刚到手的话也就只能大概看得出是啥原因导致的出错, 但是具体要说到是代码中哪行的问题导致的….就很难了…
这次正是如此, Apple把App打回来说会Crash并且扔了几个CrashReport, 大概看了下知道是网络请求回来出问题, 但是自己测试没问题而且虽然有头绪但是并不能确定原因. (我也不知道Apple用了什么鬼特殊网络反正我试了IPv6和4各种情况下都没能复现出crash, 网上查了下也有人有过这种情况不过似乎都很罕见所以难解

于是有没有办法能把CrashLog里这些内存地址实际映射到实际的代码上呢? 其实是可以的, 首先需要dSYM文件, 这个在Xcode的Organizer里找到你做好的上传到iTC里的App版本, 右键Show in Finder之后再对xcarchive文件右键Show Package Content, 这里面的dSYMs里就能找到dSYM文件啦.
找到dSYM文件之后把它拷贝出来, 同样还有Products/Applications里的你的app程序, 最后只需要准备好CrashLog就好了.
接下来需要在CrashLog里找崩溃的内存地址, 比如我图中的话崩溃虽然发生在0x6000028f3240, 然而这是线程的内存地址, 实际崩溃的地方应该是接下来一行的0x000000010056462d 0x100562000 + 9773这里, 注意这行的前面有写app的id.
最后只需要在终端中先cd到dSYM和app在的路径, 然后输入
atos -o CFDDNSS.app/Contents/MacOS/CFDDNSS -arch x86_64 -l 0x100562000 0x000000010056462d
就会提示比如:
closure #1 in connectServer(serviceUrl:completion:) (in CFDDNSS) (IPAddrAPI.swift:36)
这样就知道了问题出现在IPAddrAPI.swift文件中第36行. (结果最后查出来是Error有可能为空还!了而且还.localizedDescription了, 我自己怎么测试是没问题啦, 不知怎么的到了Apple那边就崩了╮(╯▽╰)╭
这里用的是CFDDNSS的macOS的app来进行举例, iOS也是差不多了, 同样的找到dSYM和app(注意不是ipa哦, ipa的话需要解压才能找到app)文件之后拷贝出来然后从CrashLog里找到崩溃的内存地址.
接着和上面类似执行atos命令, 比如:
atos -o MyApp.app.dSYM/Contents/Resources/DWARF/MyApp -arch arm64 -l 0x0000000000012345 0x100001234
或者
atos -arch arm64 -o ‘MyApp.app’/’MyApp’ 0x100001234
接着就会返回
specialized ViewController.tableView(UITableView, cellForRowAtIndexPath : NSIndexPath) -> UITableViewCell (in MyApp) (ViewController.swift:30)
就知道了问题出在ViewController.swift文件的第30行.
这些个从CrashLog找源码问题的方法叫Symbolicate, 以上方法都不管用的话基本就都是我这文章太老了你需要找新的方法, 关键词都提供了自己搜呗╮(╯▽╰)╭
PS: Xcode似乎针对iOS可以通过CrashLog直接Symbolicate, 不需要命令行不用查内存地址. 只需要将CrashLog拖入Xcode的Device & Simulators里的View Devices Logs里就能自动symbolicate. 也可以右键手动Re-Symbolicate.
PS2: 你知道吗? 你的App上架AppStore后可以在Xcode的Organizer里看到用户上传的CrashLog和Energy分析. 不仅不需要打开网页登录iTC而且还能直接像debugger那样直接映射回源工程里的代码哦.
=================================
追记:
最后还是出问题了, Apple给的CrashLog symbolicated之后变成了:

给出的并不是行号而是+122这样的数字, 这个其实是所谓的memory offset, 于是debug就成了很大的问题. 虽然知道了是哪个函数出的问题但是并不知道具体问题出在哪.
于是参照万能的Stackoverflow. 我们需要lldb, 首先需要像前面那样终端进入到Archives的路径, 然后
lldb -arch ARCH Products/Applications/APPNAME.app/APPNAME
像我这里就是
lldb -arch x86_64 Products/Applications/CFDDNSS.app/Contents/MacOS/CFDDNSS
如果提示找不到文件的话确认一下你的当前路径是否是在xcarchive文件的里面.
正常的话就会进入到lldb的debug模式, 接着输入
add-dsym dSYMs/APPNAME.app.dSYM/Contents/Resources/DWARF/APPNAME
像我这里就是
add-dsym dSYMs/CFDDNSS.app.dSYM/Contents/Resources/DWARF/CFDDNSS
接着执行
disassemble –name CRASHING_FUNCTION_NAME
像我这里就是
disassemble –name timeToUpdate
接着就会显示出具体问题出在哪了. 比如我的就会显示:

其中找到+112个offset就能看到具体出问题在哪了. 图中的就是问题出在第147行, 果然还是跟optional的变量被unwrap有关.