UUID.unconditionallyBridgeFromObjectiveC( :)
崩溃日志信息
最近项目收到一个类型的crash日志,感觉挺有意思。
Thread 0 Crashed:
0 libswiftFoundation.dylib 0x00000001a6161350 static UUID._unconditionallyBridgeFromObjectiveC(_:)
1 -- 0x000000010463f35c --.-.action(pageId:model:actionName:params:)
2 -- 0x000000010463db28 -.-.action(pageId:model:actionName:params:)
3 -- 0x0000000101b0c53c specialized static --.action(pageId:model:actionName:params:) + [<compiler-generated> : 0]
由于一些原因,IDFA的获取相关由OC代码改成了Swift代码实现,新版本上线,收集到一些崩溃信息。 看完崩溃日志,有这样几个问题:
-
崩溃信息集中在 iOS 14~iOS14.5之间,只发现一个 iOS14.8系统的崩溃。
-
新写的Swift代码肯定有问题!毕竟是新出现的。
-
明明Debug下,测试过很多次,怎么可能会有问题?
-
看到报错信息:
UUID._unconditionallyBridgeFromObjectiveC(_:)桥接问题,都是Swift新写的,哪有桥接OC的?
开始搜索网上信息
搜索出来许多类似的信息:
大概意思是: 使用了OC的对象,传给Swift,没有判断为nil,导致的崩溃。 例如:
// OC 注意这里返回的URL是nullable)
NSURL* url = [NSURL URLWithString:@"一个链接"];
// Swift
func handle(url: URL)
可是跟我的实现没什么关系? 我这边的实现是纯Swift的!
静下心来思考
点进去 ASIdentifierManager.shared().advertisingIdentifier.uuidString 查看系统方法:
// 节选部分
public struct UUID : ReferenceConvertible, Hashable, Equatable, CustomStringConvertible {
public typealias ReferenceType = NSUUID
/// Create a UUID from a string such as "E621E1F8-C36C-495A-93FC-0C247A3E6E5F".
/// Returns nil for invalid strings.
public init?(uuidString string: String)
/// Returns a string created from the UUID, such as "E621E1F8-C36C-495A-93FC-0C247A3E6E5F"
public var uuidString: String { get }
}
看到几个关键信息:
* public var uuidString: String { get } 返回由UUID创建的字符串(String类型,不是可选的)。 就是通过这个方法获取的UUID。
* public init?(uuidString string: String) 通过无效的字符串初始化返回一个nil。
public typealias ReferenceType = NSUUIDUUID的实现桥接的OC的NSUUID。
在 swift的代码仓库 看到这样的实现:
public static func _unconditionallyBridgeFromObjectiveC(_ source: NSUUID?) -> UUID {
var result: UUID? = nil
_forceBridgeFromObjectiveC(source!, result: &result)
return result!
}
大胆的得出结论: Swift的UUID的实现是桥接OC的NSUUID,由于未知原因,从OC拿来的NSUUID是一个nil,但是Swift转成UUID 把nil当非可选使用了。能解释这个报错信息 UUID._unconditionallyBridgeFromObjectiveC(_:)
毕竟没DEBUG复现过,只能说有可能是这个问题。不确定苹果是否修复了。
确定问题:
在 bugs.swift.org 网站上的[issues](Crash from static UUID.unconditionallyBridgeFromObjectiveC(:)) 有讨论。 看里面的描述,大概率可以确定是苹果系统内部的Bug。
问题修复
extension ASIdentifierManager {
static let zeroUUID: String = "00000000-0000-0000-0000-000000000000"
/// https://bugs.swift.org/browse/SR-6143
var safeAdvertisingIdentifier: UUID? {
return self.perform(#selector(getter: ASIdentifierManager.advertisingIdentifier))?.takeUnretainedValue() as? UUID
}
}
func getIdfa() -> String {
guard let advertisingIdentifier = ASIdentifierManager.shared().safeAdvertisingIdentifier else {
return ASIdentifierManager.zeroUUID
}
let identifier = advertisingIdentifier.uuidString
return identifier
}
修复方案的合理性
有一个做虚拟货币的三方库mopub (fork 410,Star 376),在2021年用这个方案修复过,间接的验证了方案的可行性。
简单的做个总结
-
应该是Swift和OC混编下,出现的系统bug。
-
我这边的crash,集中在iOS14.0~iOS14.5,之后一个iOS14.8的。 不知道该怎么解释。
-
这个bug,应该在Debug下没法复现(不敢保证),不记得哪个博客上看到过。