Почему возвращаемое значение String.addingPercentEncoding() необязательно?

Подпись String метод уклонения процентов:

func addingPercentEncoding(withAllowedCharacters: CharacterSet)
    -> String?

(Это было stringByAddingPercentEncodingWithAllowedCharacters в Swift 2.)

Почему этот метод возвращает необязательный?

В документации сказано, что метод возвращает nil "если преобразование невозможно", но неясно, при каких обстоятельствах выходящее преобразование может завершиться неудачей:

  • Символы экранируются с помощью UTF-8, который является полной кодировкой Unicode. Любой действительный символ Unicode может быть закодирован с использованием UTF-8 и, следовательно, может быть экранирован.

  • Я подумал, что, возможно, метод применяет какую-то проверку исправности для плохих взаимодействий между набором разрешенных символов и символами, используемыми для экранирования, но это не так: метод завершается успешно, независимо от того, содержит ли набор разрешенных символов "%", и также успешно, если разрешенный набор символов пуст.

В существующем состоянии необязательное возвращаемое значение, по-видимому, вызывает бессмысленную проверку ошибок.

2 ответа

Я подал в Apple отчет об ошибке и получил ответ - с очень полезным ответом, не меньше!

Оказывается (к моему большому удивлению), что можно успешно создавать строки Swift, которые содержат недопустимый Unicode в виде непарных суррогатных символов UTF-16. Такая строка может привести к сбою кодировки UTF-8. Вот некоторый код, который иллюстрирует это поведение:

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

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

Основанный на ответе Пола Кантрелла, небольшая демонстрация того, что этот же метод также может возвращать ноль в Objective-C, несмотря на то, что 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/ будет сбрасывать проценты так же, как:

Ошибка%20in%20convertUTF162Char%3A%20low%20surrogate%20expected%2C%20b%3D0%21%00

Другие вопросы по тегам