无法对Swift中的闭包做出弱引用

时间:2014-07-12 21:24:20

标签: swift closures objective-c-blocks weak-references retain-cycle

更新:我尝试编写它而不会使其变弱,并且似乎没有泄漏。所以也许问题不再是必要的。


在Objective-C ARC中,当你想让一个闭包能够在闭包内部使用它时,该块不能捕获对它自身的强引用,或者它将是一个保留周期,所以你可以做一个闭包捕获对自身的弱引用,如:

// This is a simplified example, but there are real uses of recursive closures
int (^fib)(int);
__block __weak int (^weak_fib)(int);
weak_fib = fib = ^(int n) {
  if (n < 2)
    return n;
  else
    return weak_fib(n-1) + weak_fib(n-2);
};

我试图将其转换为Swift:

var fib: (Int -> Int)?
fib = { [weak fib] (n: Int) in // 'weak' cannot be applied to non-class type 'Int -> Int'
  if n < 2 {
    return n
  } else {
    return fib!(n-1) + fib!(n-2)
  }
}

但是,Swift编译器不允许我声明一个弱被捕获的函数('weak' cannot be applied to non-class type 'Int -> Int')。 [unowned fib]也不起作用('unowned' cannot be applied to non-class type '(Int -> Int)?')。

我知道函数不是Swift中的类类型。但是,它们是参考类型,它们确实参与引用计数。因此,难道不应该有办法使它们成为弱或无主的参考吗?

如何在没有保留周期的Swift中编写递归闭包?

4 个答案:

答案 0 :(得分:6)

目前看来这是不可能的;你可能想要file a bug

但是你可以使用实际的func来实现同样的目的:

func fib(n: Int) -> Int {
    if n < 2 {
        return n
    } else {
        return fib(n-1) + fib(n-2)
    }
}

fib(10) // 55

计算机科学的欢乐时光!为了更直接地翻译您的代码,我们可以在Swift的内置curried函数定义的帮助下使用the Z combinator

func Z<T, U>(f: (T -> U, T) -> U)(x: T) -> U {
    return f(Z(f), x)
}

let fib = Z { (fib: Int -> Int, n: Int) in
    if n < 2 {
        return n
    } else {
        return fib(n-1) + fib(n-2)
    }
}

fib(x: 10) // 55

// (Note the name 'x' should not be required here.
//  It seems seems to be a bug in Beta 3, since the curried function in the
//  Swift guide doesn't work as advertised either.)

答案 1 :(得分:1)

看起来没有办法声明对函数的弱/无主引用;至少现在。作为解决方法,您可以将代码包装在类定义中,并unowned引用实例:

class Fib {
    @lazy var calc:(Int) -> Int = {
        [unowned self] (n: Int) -> Int in
        if n < 2 {
            return n
        } else {
            return self.calc(n-1) + self.calc(n-2)
        }
    }
}

用法:

let f = Fib()
let result = f.calc(6)

答案 2 :(得分:1)

问题在这里有详细描述:

https://xiliangchen.wordpress.com/2014/08/04/recursive-closure-and-y-combinator-in-swift/

简而言之:

  1. Swift中的递归闭包会产生强大的参考周期。
  2. Swift中没有直接的原生方法来解决这个问题。捕获列表不适用于闭包类型。
  3. 仍然有办法解决问题:Y-combinators

答案 3 :(得分:-2)

你可以得到像这样的弱引用

  

weak var = self //与weak相同,在dealloc属性上将设置为nil。所以var是可选的

或者

  

unowned(unsafe)var weakSelf = self //与untained相同