在闭包中使用数组时Swift EXC_BAD_ACCESS

时间:2014-07-31 22:22:55

标签: swift closures

考虑以下玩具示例Swift代码:

protocol Testable{}
class MyObj : Testable{}

class Test {

    var arr:[Testable] = []
    var didRun:Bool = false

    func run() -> [Testable]{
        if(didRun){
            println("arr has \(arr.count) elements")
            for e in arr{  // following access causes EXC_BAD_ACCESS
                println(e)
            }

            return arr
        } else{
            provider({ (myArr : [AnyObject]) -> () in
                self.arr = myArr as [MyObj]
                self.didRun = true
                })
            return []
        }
    }

    func provider( cb : ([AnyObject] -> ()) ){
        let a:[MyObj] = [MyObj(),MyObj(),MyObj()]
        cb(a)
    }
}

并按以下方式调用:

 let t = Test()
 t.run()
 t.run()

这会在尝试迭代返回的数组时编译但在运行时崩溃。 arr.count也是垃圾,返回一个随机的大数字,例如232521760,而arr本身指向远离0xfffffff9的某个地方,这意味着很明显它的垃圾。

我的问题是这是为什么?编译器不会抱怨类型错误。为什么我无法使用myArr数组,编译器在离开闭包后是否进行了myArr解除分配?

我可以通过将provider调用更改为:

来解决
provider({ (myArr : [AnyObject]) -> () in
    for e in myArr{
      self.arr.append(e as MyObj)
    }
    self.didRun = true
 })

但我更感兴趣的是为什么我的第一个代码不起作用。

如果有人可以向我解释Swift中的闭包语义以及为什么上面会产生这样的错误,我将不胜感激。

2 个答案:

答案 0 :(得分:3)

@SevenTenEleven上的ADF thread related to this question(Apple员工)所指示的

修改

  

看起来某些协变阵列分配存在问题;请提交一个错误,以便我们可以在编译时正确禁止它们或在运行时正确实现它们。

我们这样做, I did


在做了一些experimentsresearch之后,我得出了以下结论:

  • 这与clousures和外部范围无关
  • 只有当您向[AnyObject]投降[MyObj]
  • 时才会发生错误
  • 错误只有在"外部"变量被声明为协议类型数组

由于似乎provider始终会返回Testable,因此我可以通过更改provider函数声明并明确标记a变量来使代码正常工作数组Testable

func provider(cb: [Testable] -> ()) {
    let a : [Testable] = [MyObj(), MyObj(), MyObj()]
    cb(a)
}

然后没有必要贬低,所以没有错误。这是整个代码:

protocol Testable {}
class MyObj : Testable {}

class Test {
    var arr : [Testable] = []
    var didRun = false
    func run() -> [Testable] {
        if didRun {
            println("arr has \(arr.count) elements")
            for e in arr {
                println(e)
            }
            return arr
        } else {
            provider() { (myArr : [Testable]) in
                self.arr = myArr
                self.didRun = true
            }
            return []
        }
    }
    func provider(cb: [Testable] -> ()) {
        let a : [Testable] = [MyObj(), MyObj(), MyObj()]
        cb(a)
    }
}

let t = Test()
t.run()
t.run()

前面的代码输出:

arr has 3 elements
_TtC5hgfds5MyObj
_TtC5hgfds5MyObj
_TtC5hgfds5MyObj

答案 1 :(得分:3)

似乎swift不喜欢AnyObject或协议数组上的for-in循环。但是,如果你把它改成一个老式的for-i循环,事情就可以了。

所以而不是:

for e in arr { // causes EXC_BAD_ACCESS

只需写下:

for var i = 0; i < arr.count; ++i { // works fine
    let e = arr[i]
    ...
}