在 Swift 中,我如何访问具有动态类型检查的属性,例如 Obj-C 的 id?



我有一个函数:

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.localizedDescriptionString!(。

但是,在这种特殊情况下,您不需要使用动态消息传递。在这种情况下,首选的 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,尽管没有记录这样做。上述函数将打印SKErrorlocalizedDescription

如果您不想依赖此未记录的一致性,可以显式使其符合:

extension SKError: Error { }

由于Error声明了localizedDescription,并且SKError已经实现了localizedDescription,这种追溯一致性不需要实现它自己的。

另请注意,将未知字符串(如错误的localizedDescription(作为NSLog的第一个参数是不合适的,因为NSLog的第一个参数是格式字符串。如果字符串包含意外的%字符,则运行时行为未定义。

相关内容

最新更新