Fatal error: Insufficient space allocated to copy string contents (iOS 13) Emoji

Fatal error: Insufficient space allocated to copy string contents (iOS 13) Emoji

更新到 Xcode 11 和 iOS 13 后,我开始不断收到此错误:

Fatal error: Insufficient space allocated to copy string contents

我设法将与崩溃相关的代码缩小到这个小片段。您可以将其粘贴到 iOS 应用程序中,甚至粘贴到游乐场中,它会崩溃:

// Test. This code crashes even in a playground.
let h = 3, w = 3
// No crash without "" symbol!
let fieldStrings: [String] = ["", "3", "3", "4", "1", "2", "2", "4", "2"]
let maxLength = fieldStrings.reduce(0) { (maxLength, s) in max(maxLength, s.count) }
print(maxLength) // 1
// No crash without padding!
let paddedFieldStrings = fieldStrings.map { s in
    s.padding(toLength: maxLength, withPad: " ", startingAt: 0)
}
var lines = [String]()
for y in 0..<h {
    var line = ""
    for x in 0..<(w - 1) {
        // CRASH HERE! x = 0, y = 0
        line += paddedFieldStrings[x * h + y] + "  "
    }
    line += paddedFieldStrings[(w - 1) * h + y]
    lines.append(line)
}

原始代码负责打印游戏板的描述。它在 iOS 13 之前运行良好。我注意到这里有两点(除了字符串附加本身)是必不可少的:

  1. 表情符号"page with curl" - U+1F4C3
  2. padding(toLength:withPad)

其他表情符号也会重复崩溃。

我不认为 问题是重复的,因为我的案例不涉及任何多线程或数据库。所以,请不要标记。

在我看来这是一个错误,我会将其报告给 Apple,但现在我们需要找到解决方法。

Swift 5.

这可能确实是 Apple 方面的一个错误。

一些上下文...

不同的表情符号表示有不同的长度。例如,("" as String) 的长度(或 count)为 1,而 ("" as NSString) 的长度为 2(参见 this link更详细的解释)。

您的代码将 maxLength 设置为 1,因为数组中所有字符串(包括表情符号)的长度都解析为 1。但是,它看起来像是 padding 实现中的某个地方,表情符号的长度解析为 2。因此,当您传递表情符号和 1 的 maxLength 时,padding 返回 nil

下面是一些示例代码来帮助解释:

("" as String).count // `count` is 1
("" as NSString).length // `length` is 2

"".padding(toLength: 1, withPad: " ", startingAt: 0) // returns nil
"".padding(toLength: 2, withPad: " ", startingAt: 0) // returns ""
"".padding(toLength: 3, withPad: " ", startingAt: 0) // return " "

同样,这有点可疑,看起来确实像是 Apple 的错误。

与此同时,使用 max(maxLength, (s as NSString).length)) 计算 maxLength 或者甚至简单地确保 maxLength 大于 1 对于任何包含表情符号的数组应该可以解决崩溃问题。

解决方法:
我们都同意 - 这是一个系统错误。简单的解决方法是更改​​ maxLength 计算,将 s.count 替换为 s.utf16.count:

let maxLength = fieldStrings.reduce(0) { (maxLength, s) in max(maxLength, s.utf16.count) }

toLength小于对应的NSString长度时,padding(toLength:withPad:startingAt:)导致崩溃。 NSString.length 在这里是隐含的,因为 padding() 是 Foundation 的一部分。 utf16.count 等于对应的 NSStringlength。尽管问题隐藏在 padding() 函数中,但它以某种方式默默地 returns 一个无效的字符串,后来在 + 操作时出现崩溃。有关更多信息,请参阅 this discussion in Swift Forums