我有一个函数:
func logLocalisedDescription(_ error: Any) {
NSLog(error.localizedDescription)
}
我希望它适用于NSError和SKError,它们都有一个本地化的描述,但没有声明它的通用超类或协议。
有没有办法告诉 Swift 编译器做运行时而不是编译时类型检查,比如 Objective-C 的 id?我尝试了Any,AnyObject和AnyClass,但没有一个有效。
如果该属性不存在,我对代码在运行时失败很好。
请不要建议改造课程以采用协议或其他解决方案。我知道这些。问题是关于如何在 Swift 中进行动态类型。
使用协议:)
protocol LocalizedDescribable {
var localizedDescription: String { get }
}
extension NSError: LocalizedDescribable {}
extension SKError: LocalizedDescribable {}
func logLocalizedDescription(_ error: LocalizedDescribable) {
NSLog(error.localizedDescription)
}
你问的是向未知类型的对象发送任意的Objective-C消息。我称之为动态消息传递,而不是动态类型检查。在Objective-C中,有一些方法可以使任何对象处理任何消息,无论对象的类型如何。
Swift(在Apple平台上(允许你对AnyObject
执行Objective-C风格的动态消息传递,如AnyObject
参考中的"访问Objective-C方法和属性"中所述。在本例中,语法如下:
func logLocalizedDescription(_ error: Any) {
if let d = (error as AnyObject).localizedDescription as String? {
NSLog("%@", d)
}
}
我必须在此处明确指定消息的类型以避免"'localizedDescription'的歧义使用"错误,因为localizedDescription
有多个不同类型的定义(大多数是String
,但Progress.localizedDescription
是String!
(。
但是,在这种特殊情况下,您不需要使用动态消息传递。在这种情况下,首选的 Swift 方法是使用as? Error
,如下所示:
func logLocalizedDescription(_ error: Any) {
if let error = error as? Error {
NSLog("%@", error.localizedDescription)
} else {
NSLog("unknown error: %@", String(describing: error))
}
}
碰巧的是,SKError
已经符合Error
,尽管没有记录这样做。上述函数将打印SKError
的localizedDescription
。
如果您不想依赖此未记录的一致性,可以显式使其符合:
extension SKError: Error { }
由于Error
声明了localizedDescription
,并且SKError
已经实现了localizedDescription
,这种追溯一致性不需要实现它自己的。
另请注意,将未知字符串(如错误的localizedDescription
(作为NSLog
的第一个参数是不合适的,因为NSLog
的第一个参数是格式字符串。如果字符串包含意外的%
字符,则运行时行为未定义。