大家好这里是
Geek技术前线
。最近在打炉石过程中遇到了HSTracker
记牌器的一个闪退问题,尝试性排查了下原因。这里简单记录一下
最近炉石国服回归;由于设备限制,我基本只会在 Mac 上打炉石。并且由于主要打竞技场,所以记牌器是必不可少的辅助工具。而 Mac 上的记牌器只有HSTracker能用
但是最近使用HSTracker
记牌器却发现一个经常闪退的问题,并且重登多次又会恢复正常。
而HSTracker
工程是开源的。带着好奇心和问题实在太影响体验了,我就想着能不能本地把记牌器的代码跑起来并看看具体是什么原因导致的闪退
代码准备
按照官方的贡献指南操作
bash
# 拉取代码
git clone https://github.com/HearthSim/HSTracker.git
# 安装 swiftlint
brew install swiftlint
IDE 安装
HSTracker
是使用 Swift 开发的 macos 应用。
这里需要先进行 xcode
安装。需要注意这里由于HSTracker
有一个依赖包AppCenter
在 xcode 16
似乎编不起来(有一个头文件找不到的报错,网上也有相关的 issue,我就踩坑了),必须安装 xcode 15
代码跑起来
报错 1 No "Developer ID Application" signing certificate matching team ID
这时候 xcode 点击运行,会遇到第一个报错
这个报错官方文档也有提醒,我们只需要修改相应的 signing 信息即可
报错 2 Relove Package
卡主动不了
由于很多依赖和资源信息都是托管到 github,切换到科学上网下进行。
切换后 xcode 仍卡主在Relove Package
关闭 xcode 后命令行执行xcodebuild -resolvePackageDependencies -scmProvider system
完成后重新打开 xcode
报错 3 安装依赖报错wget command not found
macos 上默认没有 wget 命令,而记牌器构建会使用这个命令去拉取一些资源。
解决:使用brew install wget
安装wget
;由于 xcode 默认情况下的环境变量 PATH 不包含 homebrew 安装路径,需要额外使用一个软链接将 homebrew 下的 wget 软链接到 bin 目录下
bash
which wget
# /opt/homebrew/bin/wget
ln -s /opt/homebrew/bin/wget /usr/local/bin
报错 4
记牌器本身的编译产物还是基于 x86 架构。 M1 mac 上需要切换 Rosetta 模式下运行。M1 mac 上需要切换 Rosetta 模式下运行
Rosetta 是苹果公司为其基于 Apple Silicon(如 M1 和 M2 芯片)的 Mac 计算机提供的一个兼容层。它的主要功能是允许运行针对 Intel 架构编写的应用程序。Rosetta 使得开发者和用户在过渡到新的硬件架构时,能够继续使用现有的 Intel 应用程序,而不需要立即对其进行重新编译
至此,我们的记牌器终于跑起来了~
代码修复
在折腾了将近一小时才把代码跑起来之时。进入喜闻乐见的 15 分钟排队
排队完成登入后进入断点调试,直接打上 crash 断点。开一局游戏打了几个回合后很快就触发了 crash
很快发现了报错是在mirror?.getCardChoices
中,给数组插了一个空对象
通过代码排查,这个方法不是记牌器实现的方法,而是另一个 HearthMirror 库(应该是一个独立的进程用来读取炉石客户端的运行时数据)的方法给记牌器调用。当然最好的修复是解决getCardChoices
的实现,但由于由于这里 HearthMirror 本身似乎没有开源(至少在 github 也没找到相关源码)
只能尝试加 try/catch 看是否异常时捕获住还能是否运行正常。事实证明这也是能够成功的
不过这里通过尝试和查阅资料学习到了一个 iOS 开发的知识点。由于这里是 OC NSException
而 Swift 是无法直接 try/catch 捕获 OC 异常。
需要通过一个桥接 OC 方法来实现在 Swift 对 OC 方法的异常处理。在HSTracker-Bridging-Header.h
中引入桥接头文件
objectivec
// HSTracker/Utility/ExceptionCatcher.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ExceptionCatcher : NSObject
+ (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error;
@end
// HSTracker/Utility/ExceptionCatcher.h
#import "ExceptionCatcher.h"
@implementation ExceptionCatcher
+ (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error {
@try {
tryBlock();
return YES;
}
@catch (NSException *exception) {
*error = [[NSError alloc] initWithDomain:exception.name code:0 userInfo:exception.userInfo];
return NO;
}
}
@end
// HSTracker/HSTracker-Bridging-Header.h
#import "ExceptionCatcher.h"
在 swift 中对mirror?.getCardChoices()
进行异常捕获
最后问题成功修复,实测了多局也没有再复现 crash 的问题,并且mirror?.getCardChoices()
的报错本身捕获也并不会实际有记牌器功能本身上的问题
最后
最后也把这个发现通过 issue 反馈给了作者和提了个加了 try/catch 的 PR。
当然这个 PR 也不会合入,因为修复getCardChoices
的实现即可,但是这个排查的过程还是学习到了不少有趣的知识
作者也很快给了答复并且发布新版3.0.6
修复了这个问题