Can't make weak reference to closure in Swift

怎甘沉沦 提交于 2019-12-03 16:25:17

问题


Update: I tried writing it without making it weak, and there doesn't seem to be a leak. So maybe the question is no longer necessary.


In Objective-C ARC, when you want to have a closure be able to use itself inside of the closure, the block cannot capture a strong reference to itself, or it will be a retain cycle, so instead you can make the closure capture a weak reference to itself, like so:

// 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);
};

I tried to translate this to 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)
  }
}

However, the Swift compiler won't allow me to declare a function to be captured weakly ('weak' cannot be applied to non-class type 'Int -> Int'). [unowned fib] also doesn't work ('unowned' cannot be applied to non-class type '(Int -> Int)?').

I know that functions are not class types in Swift. However, they are reference types and they do participate in reference counting. Therefore, shouldn't there be a way to make them weak or unowned references?

How would I write a recursive closure in Swift that doesn't have a retain cycle?


回答1:


Looks like this isn't possible at the moment; you might want to file a bug.

But you can use an actual function to achieve the same thing:

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

fib(10) // 55

Computer science fun time! For a more direct translation of your code, we can use the Z combinator, with help from Swift's built-in curried function definitions:

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.)



回答2:


It looks like there is no way to declare weak/unowned reference to the function; at lest for now. As workaround you can wrap your code in a class definition and have unowned reference to the instance:

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)
        }
    }
}

Usage:

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



回答3:


The problem is well described here:

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

In short:

  1. Recursive closures in Swift do create strong reference cycles.
  2. There is no direct native approach in Swift to solve this problem. Capture lists don't work with closure type.
  3. Still there is a way to solve the problem: Y-combinators



回答4:


You can get weak reference like this

weak var = self // Same as weak, on dealloc property will be set to nil. So var is optional

Or

unowned(unsafe) var weakSelf = self // Same as unretained



来源:https://stackoverflow.com/questions/24717460/cant-make-weak-reference-to-closure-in-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!