为什么 String.addingPercentEncoding() 的 return 值是可选的?

Why is the return value of String.addingPercentEncoding() optional?

String 百分比转义方法的签名是:

func addingPercentEncoding(withAllowedCharacters: CharacterSet)
    -> String?

(这是 Swift 2 中的 stringByAddingPercentEncodingWithAllowedCharacters。)

为什么这个方法 return 是可选的?

文档说方法 returns nil “如果转换不可能”,但不清楚在什么情况下转义转换会失败:

就目前而言,非可选 return 值似乎在强制进行无意义的错误检查。

我就此向 Apple 提交了一份错误报告,并收到了回复 — 非常有帮助的回复,一点也不逊色!

事实证明(令我惊讶的是)可以成功创建 Swift 字符串,其中包含未配对 UTF-16 surrogate chars 形式的无效 Unicode。这样的字符串会导致 UTF-8 编码失败。下面是一些说明此行为的代码:

// Succeeds (wat?!):
let str = String(
    bytes: [0xD8, 0x00] as [UInt8],
    encoding: .utf16BigEndian)!

// Returns nil:
str.addingPercentEncoding(withAllowedCharacters: .alphanumerics)

基于 Paul Cantrell 的回答,小规模的演示表明同样的方法也可以在 Objective-C 中 return null,尽管 String 和 NSString 在编码方面是不同的野兽:

uint8_t bytes[2] = { 0xD8, 0x00 };
NSString *string = [[NSString alloc] initWithBytes:bytes length:2 encoding:NSUTF16BigEndianStringEncoding];
// \ud800
NSLog(@"%@", string);

NSString *escapedString = [string stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLHostAllowedCharacterSet];
// (null)
NSLog(@"%@", escapedString);

为了好玩,https://r12a.github.io/app-conversion/ 将百分比转义为:

Error%20in%20convertUTF162Char%3A%20low%20surrogate%20expected%2C%20b%3D0%21%00