Search code examples
swiftgenericsexc-bad-access

How can Xcode successfully access data the program cannot?


I'm running Xcode 9, Swift 4, High Sierra. In my code I have a mapping that, cut out and stripped down, includes this as the first lines in the closure:

let foo = graph.edges.map { (edge: Edge<Node>) -> (Int, Int) in
    print(edge)
    let fromvertex = (edge.from as? VertexElement)
        .
        .
        .
}

There's a breakpoint on the line let from vertex ...

When run, the code is successful up to the breakpoint, which is where the problem starts. If at that point I execute po edge.from in the debugger, I get the expected result. If I step one line, and so execute the assignment, I get Thread 1: EXC_BAD_ACCESS (code=2, address=*whatever*). I can then execute po edge.from or even po (edge.from as? VertexElement) at that point and still get the expected results.

How can this be? There's no optional involved in edge.from, and I'd think the data is there correctly since the debugger finds it.

Edge is (as you'd expect) a generic class, with the first part of the definition being this:

public class Edge<T>: NSObject, NSCoding, Codable where T: Hashable {
    public typealias Weight = SimWeight

    public let from: Vertex<T>
    public let to: Vertex<T>
    public var weight: Weight? = nil
        .
        .
        .
}

and then, the first part of Vertex, also a generic, looks like this:

public struct Vertex<T>: Equatable, Codable where T: Hashable {
    public var data: T
    public let index: Int
    public var gridX: Int?
    public var gridY: Int?
        .
        .
        .
}

Node is another, concrete class (not a generic)

Finally, I have address sanitization turned on.

All that said, I neither can see where the exception is coming from, nor why the debugger can get the data when the program cannot. Any ideas what's wrong (or even how to debug it?)


Solution

  • I can only suspect a compiler problem, because it's fixed now by reorganizing the code. Specifically, I created this method within Edge:

    func indirectEdge() throws -> IndirectEdge {
        guard let fromvx = self.from as? VertexElement,
            let tovx = self.to as? VertexElement,
            let weight = self.weight else {throw TypeError.notVertex}
        return IndirectEdge(from: fromvx.index, to: tovx.index, weight: weight)
    }
    

    and then changed the code at the top of this post to be:

    guard let indirectEdges = try? graph.edges.map({ (edge) throws -> IndirectEdge in
        return try edge.indirectEdge()
    }) else {throw SimFileError.unimplementedOperation(why: "Don't know how to encode this graph")}
        .
        .
        .
    

    At that point everything began to work, without the exceptions. (yes, I changed the later code to accept the IndirectEdge; that code is further on and shouldn't have any effect.) What I expected was that the exception would move into the indirectEdge method, but that's not what happened. It's good the code works now, but scary that I can't figure out why.