我遇到了一个特殊的问题。在很多年前的一个项目中,有这样一段代码:
func testFunc() {
// ......
let runs = CTLineGetGlyphRuns(finalLine) as! [CTRun]
if let truncationRun = runs.last {
var truncationPosition = CGPoint.zero
CTRunGetPositions(truncationRun, CFRange(location: 0, length: 0), &truncationPosition)
var truncationAscent: CGFloat = 0, truncationDescent: CGFloat = 0
let truncationWidth = CTRunGetTypographicBounds(truncationRun, CFRange(location: 0, length: 0), &truncationAscent, &truncationDescent, nil)
// ......
}
// ......
}
当 truncationRun 中的字形数量超过一定值时,函数执行结束后会发生崩溃。这个值在不同的iOS系统上有所不同——在iOS 13上,它是11;在 iOS 15 上,为 7;在 iOS 17 上,它是 8。我不明白为什么会发生这种情况?
当然,如果根据CTRunGetGlyphCount()方法为trancationPosition控件分配空间,则这段代码可以正常运行。
我假设你的问题是“为什么当运行时间很短时不会发生崩溃?”在一般情况下,此代码肯定会崩溃,因为它尝试将 CGPoint 数组写入为单个 CGPoint 分配的空间中。这会损坏其他内存并导致崩溃。经典的缓冲区溢出。
但至于为什么在某些情况下不会崩溃,很大程度上取决于内存中紧接着
truncationPosition
之后发生的事情。你破坏的记忆里可能什么都没有。它可能包含一些不“重要”的东西。由于对齐要求,它可能是空的。溢出越大,您就越有可能遇到重要的事情或破坏堆栈本身。
但简短的答案是,超出缓冲区的写入是未定义的行为,并且系统不保证它会崩溃。通常它会,但通常它可能只是做奇怪的事情,甚至“起作用”。操作系统版本之间,甚至同一版本上相同代码的运行之间,发生的情况可能会发生变化。