1

i searched the swift spec for capture list on inner func but without luck, is there any way to break these kind of reference cycle?

class Hello {
    var name = "name"
    var fn: (()->())? = nil
}

func foo() -> Hello? {
    var bar: Hello? = Hello()
    func wrapper() -> () -> () {
        func inner() {
            print("bar: \(bar)")
        }
        return inner
    }
    bar?.fn = wrapper()
    return bar
}

var s = foo()
var b = Hello()

isKnownUniquelyReferenced(&s)  // false
isKnownUniquelyReferenced(&b)  // true
Minami
  • 963
  • 6
  • 21
  • 2
    Not if you say `func`. Rewrite `inner` as an anonymous function. `let inner : () -> () = {`. Now you can use a capture list. – matt Feb 28 '19 at 11:28
  • thx for your quick reply @matt, although inner function is a rare use case in practice , I'll treat lacking capture list on func declaration a mistake of swift which break the orthogonality – Minami Feb 28 '19 at 11:47
  • I agree but it’s easy to rewrite as I suggested. – matt Feb 28 '19 at 11:57
  • Another option is to rebind `bar` as `weak` outside of the nested function, e.g `weak var bar = bar; func inner() { print("bar: \(bar)") }`. It's worth noting that in general a capture list can always be replaced by a new variable outside of the closure which then gets captured. The syntactic sugar of a capture list is nice though. – Hamish Feb 28 '19 at 11:59
  • @Hamish But you couldn’t do unowned that way. And your trick wouldn’t work for a func defined at top level. It seems to me that the capture list is not mere syntactic sugar at all; it is crucial and unique, and the OP is right to complain that it applies only to anonymous functions. – matt Feb 28 '19 at 12:38
  • @matt [Sure you can](https://gist.github.com/hamishknight/8b1c3d2726db4ddeaffe3a28f33bfdc4) :) – Hamish Feb 28 '19 at 13:26
  • @Hamish You win this time, Batman! :) – matt Feb 28 '19 at 13:29
  • @matt Ah actually one situation where it doesn't work is when used as a global and then you get an unapplied reference to the function (because of lazy evaluation that causes the captures to get evaluated at different times): https://gist.github.com/hamishknight/3c346476a5c8b896c10c174f82770ee4. So okay, not quite always, but in the vast majority of cases. I don't disagree with being able to add capture lists to ordinary functions though :) – Hamish Feb 28 '19 at 13:40

1 Answers1

2

To use a capture list, you have to use an anonymous function (what many people wrongly call a "closure"). So, you would rewrite your

    func inner() {
        print("bar: \(bar)")
    }

as

    let inner : () -> () = { [weak bar] in
        print("bar: \(bar)")
    }
matt
  • 515,959
  • 87
  • 875
  • 1,141