如何解决String(format:)传参少于占位符时M1等设备的运行时崩溃问题
问题根因说明
该崩溃是ARM架构(M1芯片、iOS物理真机均为ARM架构)的可变参数传递机制导致的:x86架构下缺失格式化参数时会默认读取栈上的空值,大概率不会触发立即崩溃,但ARM架构对栈内存访问的校验更严格,参数数量和占位符不匹配时会直接触发非法内存访问崩溃。
可落地解决方案
1. 开启编译期格式化参数检查(最快落地)
- 在Xcode的Build Settings中找到以下两个编译选项,全部设置为Yes:
Typecheck calls to printf/scanf(对应编译参数-Wformat)Treat missing format string arguments as errors(对应编译参数-Werror=format-args)
- 如果你的格式化字符串是通过
NSLocalizedString读取的,需要给自定义的封装方法加上编译检查宏修饰:// OC头文件定义宏,Swift可以用@_semantics注解触发检查 #define LocalizedStringWithFormat(key, ...) \ [NSString localizedStringWithFormat:NSLocalizedString(key, nil), ##__VA_ARGS__] NS_FORMAT_FUNCTION(1,2)
2. 用Swift原生类型安全格式化(推荐,Swift 5.5+支持)
Swift 5.5之后引入了类型安全的本地化字符串语法,会在编译期自动检查参数数量和类型是否匹配,完全避免这类问题:
// 旧写法无编译检查 let b = NSLocalizedString("1-> %@, 2-> %@", comment: "") let bStr = String(format: b, "hello") // 编译不报错,运行崩溃 // 新类型安全写法 func getTwoParamLocalizedStr(p1: String, p2: String) -> String { String(localized: "1-> \(p1), 2-> \(p2)") } // 调用时少传参数直接编译报错 let bStr = getTwoParamLocalizedStr(p1: "hello") // 编译错误:缺少参数p2
3. 自定义安全格式化兜底(防止极端漏网场景)
如果无法升级Swift版本或者需要兼容旧代码,可以封装安全的格式化方法,运行时校验参数数量,就算不匹配也只会打日志不会崩溃:
extension String { static func safeFormat(_ format: String, args: CVarArg...) -> String { // 统计格式化字符串中的占位符数量 let pattern = "%[\\d\\.]*[@dfgsx]" let regex = try! NSRegularExpression(pattern: pattern, options: []) let placeholderCount = regex.numberOfMatches(in: format, options: [], range: NSRange(location: 0, length: format.count)) // 参数不足时补空值兜底,同时上报错误日志 var safeArgs = args while safeArgs.count < placeholderCount { safeArgs.append("") print("⚠️ 格式化参数不足:格式串\(format) 期望\(placeholderCount)个参数,实际传入\(args.count)个") } return String(format: format, arguments: safeArgs) } } // 使用示例 let bStr = String.safeFormat(b, args: "hello") // 不会崩溃,缺的参数自动补空字符串
内容的提问来源于stack exchange,提问作者Konstantinos Nikoloutsos
相关产品推荐
相关产品推荐

