For some time now Xcode has been downloading crash reports from users of my app about crashes related to arrays. One of them looks like this:
...
Code Type: ARM-64
Parent Process: launchd [1]
User ID: 501
Date/Time: 2024-07-18 14:59:40.4375 +0800
OS Version: macOS 15.0 (24A5289h)
...
Crashed Thread: 0
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x00000001045048b8
Termination Reason: Namespace SIGNAL, Code 5 Trace/BPT trap: 5
Terminating Process: exc handler [1771]
Thread 0 Crashed:
0 MyApp 0x00000001045048b8 specialized Collection.map<A>(_:) + 596
1 MyApp 0x00000001045011e4 MyViewController.validateToolbarButtons() + 648 (MyViewController.swift:742)
...
The relevant code looks like this:
class MyViewController {
func validateToolbarButtons() {
let indexes = tableView.clickedRow == -1 || tableView.selectedRowIndexes.contains(tableView.clickedRow) ? tableView.selectedRowIndexes : IndexSet(integer: tableView.clickedRow)
let items = indexes.map({ myArray[$0] })
...
}
}
The second crash looks like this:
...
Code Type: X86-64 (Native)
Parent Process: launchd [1]
User ID: 502
Date/Time: 2024-07-15 15:53:35.2229 -0400
OS Version: macOS 15.0 (24A5289h)
...
Crashed Thread: 0
Exception Type: EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes: 0x0000000000000001, 0x0000000000000000
Termination Reason: Namespace SIGNAL, Code 4 Illegal instruction: 4
Terminating Process: exc handler [13244]
Thread 0 Crashed:
0 libswiftCore.dylib 0x00007ff812904fc0 _assertionFailure(_:_:flags:) + 288
1 MyApp 0x0000000101a31e04 specialized _ArrayBuffer._getElementSlowPath(_:) + 516
2 MyApp 0x00000001019d04eb MyObject.myProperty.setter + 203 (MyObject.swift:706)
3 MyApp 0x000000010192f66e MyViewController.controlTextDidChange(_:) + 190 (MyViewController.swift:166)
...
And the relevant code looks like this:
class MyObject {
var myProperty: [MyObject] {
get {
...
}
set {
let items = newValue.map({ $0.id })
...
}
}
}
What could cause such crashes? Could they be caused by anything other than concurrent access from multiple threads (which I'm quite sure is not the case here, as I only access these arrays from the main thread)?
While these crashes have different codes, they’re likely to be caused by the some immediate issue: a Swift runtime trap. See EXC_BREAKPOINT (SIGTRAP) and EXC_BAD_INSTRUCTION (SIGILL). You get different exception codes because the first crash is on Apple silicon and the second on Intel.
Regarding the first crash, the most likely cause is this: myArray[$0]
. If $0
is out of bounds, the array access will trap.
The second trap is trickier. Note how frame is _getElementSlowPath(…)
. This suggests that you’re working with a non-native array, like an NSArray
. Those do lazy type enforcement, so the setter will check that the element type is correct and trap if it’s not. I think that’s what you’re hitting here.
Consider this program:
import Foundation
class MyElement {
let myProperty: Int
init(myProperty: Int) {
self.myProperty = myProperty
}
}
func printElements(ns: NSArray) {
print("before cast")
let a = ns as! [MyElement]
print("after cast")
for e in a {
print(e.myProperty)
}
}
func main() {
let ns = NSMutableArray()
ns.add(MyElement(myProperty: 42))
ns.add(MyElement(myProperty: 6))
ns.add("Hello Cruel World!" as NSString)
ns.add(MyElement(myProperty: 7))
printElements(ns: ns)
}
main()
When I run it on my Mac (Xcode 16.0b3 on macOS 14.5) I see this output:
before cast
after cast
42
6
Fatal error: NSArray element failed to match the Swift Array Element type
Expected MyElement but found __NSCFString
This shows how the ns as! [MyElement]
cast is done lazily, only triggering a trap when you try to access the element that’s the wrong type.
Notably, the crashing thread backtrace looks like this:
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = Fatal error: NSArray element failed to match the Swift Array Element type
Expected MyElement but found __NSCFString
frame #0: 0x00000001a53f9890 libswiftCore.dylib`_swift_runtime_on_report
frame #1: 0x00000001a54b8c74 libswiftCore.dylib`_swift_stdlib_reportFatalError + 176
frame #2: 0x00000001a508b114 libswiftCore.dylib`function signature specialization <Arg[1] = [Closure Propagated : closure #1 (Swift.UnsafeBufferPointer<Swift.UInt8>) -> () in closure #1 (Swift.UnsafeBufferPointer<Swift.UInt8>) -> () in Swift._assertionFailure(_: Swift.StaticString, _: Swift.String, flags: Swift.UInt32) -> Swift.Never, Argument Types : [Swift.UnsafeBufferPointer<Swift.UInt8>Swift.UInt32]> of generic specialization <()> of Swift.String.withUTF8<τ_0_0>((Swift.UnsafeBufferPointer<Swift.UInt8>) throws -> τ_0_0) throws -> τ_0_0 + 156
frame #3: 0x00000001a5062c84 libswiftCore.dylib`Swift._assertionFailure(_: Swift.StaticString, _: Swift.String, flags: Swift.UInt32) -> Swift.Never + 180
frame #4: 0x00000001a5064c38 libswiftCore.dylib`Swift._ArrayBuffer._getElementSlowPath(Swift.Int) -> Swift.AnyObject + 2056
frame #5: 0x00000001a506b6d8 libswiftCore.dylib`Swift.Array.subscript.read : (Swift.Int) -> τ_0_0 + 304
frame #6: 0x00000001a506b574 libswiftCore.dylib`protocol witness for Swift.Collection.subscript.read : (τ_0_0.Index) -> τ_0_0.Element in conformance Swift.Array<τ_0_0> : Swift.Collection in Swift + 68
frame #7: 0x00000001a5055e4c libswiftCore.dylib`Swift.IndexingIterator.next() -> Swift.Optional<τ_0_0.Element> + 668
* frame #8: 0x0000000100001a64 Test`printElements(ns=4 elements) at main.swift:14:5
frame #9: 0x00000001000016d0 Test`main() at main.swift:25:5
frame #10: 0x000000010000159c Test`main at main.swift:28:1
frame #11: 0x0000000194ffe0e0 dyld`start + 2360
Frames 4 and 3 should ring some bells.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"