让我们从它的源代码来探索它,因为 Swift Foundation 是开源的。
URLComponents
初始化器在 apple/swift – URLComponents.swift 和 apple/swift-corelibs-foundation – URLComponents.swift 中实现,并简单地调用NSURLComponents
.
NSURLComponents
初始化器在apple /swift-corelibs-foundation - NSURL.swift中实现,并且简单地调用_CFURLComponentsCreateWithURL
.
_CFURLComponentsCreateWithURL
在apple/swift-corelibs-foundation – CFURLComponents.c中实现并执行以下操作:
- 一个失败的副本
CFURLCopyAbsoluteURL
- 一个失败的创作,
_CFURLComponentsCreateWithString
它调用:
_CFURIParserParseURIReference
+ 失败的_CFURIParserURLStringIsValid
CFURLCopyAbsoluteURL
在apple/swift-corelibs-foundation – CFURL.c中实现,仅在以下情况下失败:
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
if ( base && CFURLIsFileReferenceURL(base) && !CFURLHasDirectoryPath(base) ) {
// 16695827 - If the base URL is a file reference URL which doesn't end with a slash, we have to convert it to a file path URL before we can make it absolute.
base = CFURLCreateFilePathURL(alloc, base, NULL);
if ( !base ) {
// could not convert file reference URL to file path URL -- fail will NULL
return NULL;
}
}
#endif
的实现CFURLCreateFilePathURL
在opensource.apple.com/source/CF – CFURL.c中,我的理解是只有在没有方案或没有路径的情况下才会失败,这应该是不可能的,因为我们之前测试了一个文件方案或文件存在CFURLIsFileReferenceURL
。
_CFURIParserParseURIReference
在apple/swift-corelibs-foundation – CFURLComponents_URIParser.c中实现,并且仅当 URL 长度超过 2 GB 时才会失败,我认为这与 RFC 规范无关。
_CFURIParserURLStringIsValid
本质上将调用_CFURIParserValidateComponent
每个组件,并因无效字符或转义序列而失败。这可能是最相关的部分。
现在,通过一些实验,我们知道我们需要一个方案(例如,https://
或者简单地说a://
),并且我们使用保留字符来提出示例,例如:
// OK
let url = URL(string: "a://@@")!
// CRASH
let components = URLComponents(url: url, resolvingAgainstBaseURL: true)!
尝试替代初始化器URLComponents
也会失败,所以不要试图认为它是不同的:
// CRASH
let components = URLComponents(string: url.absoluteString)!
结论
"a://@@"
是一个有效的 NSURL 但无效的 RFC 3986 的例子。
在旁注中,一些 Swift 人似乎希望未来统一对 URL 和 URLComponents 的支持(不再有 RFC 差异),如 URL.swift 所示:
// 未来的实现说明:
// NSURL(实际上是 CFURL,它提供了它的实现)在处理一些更深奥(和一些不那么深奥)的字符串时有很多怪癖。我们希望将其中的大部分内容转移到更现代的 NSURLComponents 中,但是二进制兼容性问题使这变得困难。
// 希望很快,我们可以将下面的一些对 NSURL 的委托替换为对 NSURLComponents 的委托。它不能零碎完成,否则我们会从 API 中得到不一致的结果。
我不确定他们打算如何做到这一点,因为这意味着要么URL(string: "a://@@")
失败,要么URLComponents(string: "a://@@")
成功。