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"