A lot of times, I'll receive a Swift Error
object from a framework, which is really an NSError
.
很多时候,我会从框架中收到一个Swift Error对象,这实际上是一个NSError。
In order to access its information (e.g. code
), I need to cast it to an NSError
:
为了访问它的信息(例如代码),我需要将其转换为NSError:
(error as NSError).code == ....
Why is this just an unconditional as
? If I design my own error class that conforms to Error
, it won't necessarily be an NSError
, so how can this be the correct way to perform this cast?
为什么这只是一个无条件的?如果我设计自己的符合Error的错误类,它不一定是NSError,那么这怎么可能是执行此转换的正确方法呢?
Is there some kind of special case in the type system? This is a downcast that behaves like an upcast.
类型系统中是否存在某种特殊情况?这是一个贬低行为,就像一个向上倾斜。
2 个解决方案
#1
5
I believe the capability for Error
to be convertible to NSError
is hardcoded into the compiler, and the actual bridging is implemented in the Swift runtime.
我相信可以将Error转换为NSError的功能被硬编码到编译器中,并且实际的桥接在Swift运行时中实现。
In runtime/ErrorObject.mm
, I found this comment:
在runtime / ErrorObject.mm中,我发现了这条评论:
// This implements the object representation of the standard Error
// type, which represents recoverable errors in the language. This
// implementation is designed to interoperate efficiently with Cocoa libraries
// by:
// - ...
// - allowing a native Swift error to lazily "become" an NSError when
// passed into Cocoa, allowing for cheap Swift to Cocoa interop
And this function:
而这个功能:
/// Take an Error box and turn it into a valid NSError instance.
id
swift::_swift_stdlib_bridgeErrorToNSError(SwiftError *errorObject) {
...
// Otherwise, calculate the domain, code, and user info, and
// initialize the NSError.
auto value = SwiftError::getIndirectValue(&errorObject);
auto type = errorObject->getType();
auto witness = errorObject->getErrorConformance();
NSString *domain = getErrorDomainNSString(value, type, witness);
NSInteger code = getErrorCode(value, type, witness);
NSDictionary *userInfo = getErrorUserInfoNSDictionary(value, type, witness);
...
}
The ErrorHandling.rst document says this about the rationale:
ErrorHandling.rst文档说明了这个基本原理:
It should be possible to turn an arbitrary Swift enum that conforms to
Error
into anNSError
by using the qualified type name as the domain key, the enumerator as the error code, and turning the payload into user data.应该可以通过使用限定类型名称作为域密钥,将枚举数作为错误代码,并将有效负载转换为用户数据,将符合Error的任意Swift枚举转换为NSError。
(Parts of the document may be outdated.)
(部分文件可能已过时。)
And this is (I think) at least one part in the type checker were the information that Error
is convertible to NSError
is encoded (there are probably more):
这是(我认为)类型检查器中至少有一部分是可以转换为NSError的信息被编码(可能还有更多):
// Check whether the type is an existential that contains
// Error. If so, it's bridged to NSError.
if (type->isExistentialWithError()) {
if (auto nsErrorDecl = getNSErrorDecl()) {
// The corresponding value type is Error.
if (bridgedValueType)
*bridgedValueType = getErrorDecl()->getDeclaredInterfaceType();
return nsErrorDecl->getDeclaredInterfaceType();
}
}
#2
2
This is a great question.
这是一个很好的问题。
I thought I saw "An Error type can be bridged to an NSError" somewhere, but that must have been Xcode or some tutorial online.
我以为我在某处看到“错误类型可以桥接到NSError”,但这必须是Xcode或在线教程。
Luckily I found this from swift/NSError.swift.
幸运的是我从swift / NSError.swift中找到了这个。
// NSError and CFError conform to the standard Error protocol. Compiler
// magic allows this to be done as a "toll-free" conversion when an NSError
// or CFError is used as an Error existential.
extension NSError : Error {
@nonobjc
public var _domain: String { return domain }
@nonobjc
public var _code: Int { return code }
@nonobjc
public var _userInfo: AnyObject? { return userInfo as NSDictionary }
/// The "embedded" NSError is itself.
@nonobjc
public func _getEmbeddedNSError() -> AnyObject? {
return self
}
}
extension CFError : Error {
public var _domain: String {
return CFErrorGetDomain(self) as String
}
public var _code: Int {
return CFErrorGetCode(self)
}
public var _userInfo: AnyObject? {
return CFErrorCopyUserInfo(self) as AnyObject
}
/// The "embedded" NSError is itself.
public func _getEmbeddedNSError() -> AnyObject? {
return self
}
}
#1
5
I believe the capability for Error
to be convertible to NSError
is hardcoded into the compiler, and the actual bridging is implemented in the Swift runtime.
我相信可以将Error转换为NSError的功能被硬编码到编译器中,并且实际的桥接在Swift运行时中实现。
In runtime/ErrorObject.mm
, I found this comment:
在runtime / ErrorObject.mm中,我发现了这条评论:
// This implements the object representation of the standard Error
// type, which represents recoverable errors in the language. This
// implementation is designed to interoperate efficiently with Cocoa libraries
// by:
// - ...
// - allowing a native Swift error to lazily "become" an NSError when
// passed into Cocoa, allowing for cheap Swift to Cocoa interop
And this function:
而这个功能:
/// Take an Error box and turn it into a valid NSError instance.
id
swift::_swift_stdlib_bridgeErrorToNSError(SwiftError *errorObject) {
...
// Otherwise, calculate the domain, code, and user info, and
// initialize the NSError.
auto value = SwiftError::getIndirectValue(&errorObject);
auto type = errorObject->getType();
auto witness = errorObject->getErrorConformance();
NSString *domain = getErrorDomainNSString(value, type, witness);
NSInteger code = getErrorCode(value, type, witness);
NSDictionary *userInfo = getErrorUserInfoNSDictionary(value, type, witness);
...
}
The ErrorHandling.rst document says this about the rationale:
ErrorHandling.rst文档说明了这个基本原理:
It should be possible to turn an arbitrary Swift enum that conforms to
Error
into anNSError
by using the qualified type name as the domain key, the enumerator as the error code, and turning the payload into user data.应该可以通过使用限定类型名称作为域密钥,将枚举数作为错误代码,并将有效负载转换为用户数据,将符合Error的任意Swift枚举转换为NSError。
(Parts of the document may be outdated.)
(部分文件可能已过时。)
And this is (I think) at least one part in the type checker were the information that Error
is convertible to NSError
is encoded (there are probably more):
这是(我认为)类型检查器中至少有一部分是可以转换为NSError的信息被编码(可能还有更多):
// Check whether the type is an existential that contains
// Error. If so, it's bridged to NSError.
if (type->isExistentialWithError()) {
if (auto nsErrorDecl = getNSErrorDecl()) {
// The corresponding value type is Error.
if (bridgedValueType)
*bridgedValueType = getErrorDecl()->getDeclaredInterfaceType();
return nsErrorDecl->getDeclaredInterfaceType();
}
}
#2
2
This is a great question.
这是一个很好的问题。
I thought I saw "An Error type can be bridged to an NSError" somewhere, but that must have been Xcode or some tutorial online.
我以为我在某处看到“错误类型可以桥接到NSError”,但这必须是Xcode或在线教程。
Luckily I found this from swift/NSError.swift.
幸运的是我从swift / NSError.swift中找到了这个。
// NSError and CFError conform to the standard Error protocol. Compiler
// magic allows this to be done as a "toll-free" conversion when an NSError
// or CFError is used as an Error existential.
extension NSError : Error {
@nonobjc
public var _domain: String { return domain }
@nonobjc
public var _code: Int { return code }
@nonobjc
public var _userInfo: AnyObject? { return userInfo as NSDictionary }
/// The "embedded" NSError is itself.
@nonobjc
public func _getEmbeddedNSError() -> AnyObject? {
return self
}
}
extension CFError : Error {
public var _domain: String {
return CFErrorGetDomain(self) as String
}
public var _code: Int {
return CFErrorGetCode(self)
}
public var _userInfo: AnyObject? {
return CFErrorCopyUserInfo(self) as AnyObject
}
/// The "embedded" NSError is itself.
public func _getEmbeddedNSError() -> AnyObject? {
return self
}
}