Search code examples
iosswiftexc-bad-accessxcode6xcode6gm

Assigning array property is always going to crash EXC_BAD_ACCESS


I cannot understand why following code every time crashes on Xcode 6 GM using Swift. Could you help me to understand this issue?

Thank you in advance.

OptionsToSelect.swift

import Foundation

struct OptionToSelect {
    var value : Any
    var desc : String
    var available : Bool
}

SomeClass.swift

import Foundation

class SomeClass {
    var items = Array<OptionToSelect>()
}

ViewController.swift

    override func viewDidLoad() {
        super.viewDidLoad()
        var c = SomeClass()
        c.items = [ /// <------------- __ EXC_BAD_ACCESS here, why?! __
            OptionToSelect(value: 1, desc: "A", available: true),
            OptionToSelect(value: 2, desc: "B", available: true)
        ]
    }

Edit 1 On twitter I got answer that it's related to Any, and indeed. But why?


Solution

  • The compiler still has various problems with Any, so the short answer is "bug in Swift." It also has trouble if value is a protocol. I suspect that it has trouble figuring out how to make a copy; but that's a guess.

    But you should strongly avoid using Any here. You almost never should use Any. In most cases, you want a generic:

    struct OptionToSelect<T> {
      let value : T
      let desc : String
      let available : Bool
    }
    

    (The way you're using it, let seems more appropriate than var here; do you really need to change these values?)

    This does require that the entire array have the same kinds of values:

    var items = [OptionToSelect<Int>]()
    

    But that's usually correct anyway. If it isn't; if you need a mix of values, then you should consider what protocol all the values might conform to. That won't actually solve your problem (Swift will crash on a protocol here too), but the design will be much better than Any. If the best you can say about the type is "well, it's something," you're going to get a lot of complicated (and sometimes dangerous) down-casting code. You'll fight it every time you turn around.

    To use a protocol here (or Any) you can either make OptionToSelect a class (which is the easiest answer), or hide the problem in a box:

    struct OptionToSelect {
      let value : AnyBox
      let desc : String
      let available : Bool
    }
    
    final class AnyBox {
      let value: Any
      init (_ value: Any) { self.value = value }
    }
    
    [
      OptionToSelect(value: AnyBox(1), desc: "A", available: true),
      OptionToSelect(value: AnyBox(2), desc: "B", available: true)
    ]
    

    (The same technique is needed for protocol types.)