多くの場合、フレームワークからSwift Error
オブジェクトを受け取ります。これは、実際にはNSError
です。
その情報(例:code
)にアクセスするには、それをNSError
にキャストする必要があります。
(error as NSError).code == ....
なぜこれは無条件のas
なのですか? Error
に準拠する独自のエラークラスを設計した場合、それは必ずしもNSError
であるとは限りません。では、これがこのキャストを実行する正しい方法であるとしたらどうでしょうか。
型システムに何らかの特殊なケースはありますか?これは、アップキャストのように動作するダウンキャストです。
Error
をNSError
に変換できる機能はコンパイラにハードコードされており、実際のブリッジングはSwiftランタイムに実装されていると思います。
で 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
そして この関数 :
/// 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);
...
}
ErrorHandling.rst document は、理論的根拠について次のように述べています。
修飾型名をドメインキーとして使用し、列挙子を次のように使用することで、
Error
に準拠する任意のSwift列挙型をNSError
に変換できるはずです。エラーコード、およびペイロードをユーザーデータに変換します。
(ドキュメントの一部が古くなっている可能性があります。)
そして これは (私は思う)タイプチェッカーの少なくとも1つの部分は、Error
が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();
}
}
これは素晴らしい質問です。
「エラータイプはNSErrorにブリッジできる」とどこかで見たと思いましたが、それはXcodeまたはオンラインのチュートリアルであったに違いありません。
幸いなことに、これは 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
}
}