Strong Reference Cycle in Recursive Closures

It is a very common technique to create generic recursive closures in swift, like that demonstrated in WWDC2014 Advanced Swift Session.

But somehow I find it is very possibile to create a Strong Reference Cycle with it.


Here is the simple code. I used the foo function scope to simulate life cycle, beyond which all objects are supposed to dealloc.


class Nested {
    func print() {
        println("Print Nested")
    }
   
    deinit {
        println("Nested dealloc")
    }
}

typealias ClosureType = Int -> Int

func foo() {
    var myClosure : ClosureType = {
        value in value
    }
   
    func setClosure() {
        var date = Nested()
        myClosure = {
            input in
            date.print()
            return input <= 1 ? 1 : input + myClosure(input - 1)
        }
    }
   
    // do something
    setClosure()
    println(myClosure(5))
}

// test it
foo()


the output didn't include the deinit string


I think it is because myClosure is capturing itself. It is not a very big memory leak until some extra objects are involved (like that Nested object which may contain very large amount of objects).The problem is when I no longer need that closure, I can't find anyway to dealloc it.


There are some workaround, for example: you can add another layer between the closure and the caller, so that the strong reference cycle occurs between the mid-layer and the closure rather than inside that closure itself, making it possible for you to free the closure manually.


But in this certain case I can't find any of them elegant :you cannot sepecify a weak reference to a closure at the moment, nor can you add a capture list for a closure outside class definitions.


Hope anyone can give me a solution.

You aren't ever throwing the old closure away. It isn't a reference cycle, but memory will grow each time you call foo().


foo() and myClosure look like globals in this context. Whenever you call setClosure(), the new closure you make includes the last closure you made (it can't run it until it has a value for 'input'), so it is holding on to the old closure inside the new one. It isn't a reference cycle because if you ever set myClosure to something that doesn't include the last value, all of the previous closures will get deallocated.


Weakness wouldn't help you in this case, because you need the old closure to stay around to get the recursive behavior. If you tell us the effect you are trying to generate, we might be able to help you find a more memory efficient way to accomplish it.

You aren't ever throwing the old closure away.

That is true, but the problem is ,when I go out of foo() scope, I don't have any reference to that closure, and there is no way I can release it, and all data in Nested is leaked.This is exactly a strong reference cycle.


Weakness wouldn't help you in this case

I don't think so, if I can caputre myClosure as weak in that closure, I can always use this closure until foo() returns.


As you posted, if I replace the content of myClosure with another closure, the old closure will be deallocated. But think of this situation: I am writing this foo function to return myClosure. The closure will be leaked forever.

I hope you can tell me how to avoid this.

Does this work? (coded in the browser + untested):


func foo(clear clear:Bool = false) {

     var myClosure = { value in value }


     func setClosure() { 
        var date = Nested() 
        myClosure = { 
            input in 
            date.print() 
            return input <= 1 ? 1 : input + myClosure(input - 1) 

       } 
    }

     func clearClosure(){
          myClosure = { value in value }
          print("clearing")
     }

     //Do Something
     if !clear {
          setClosure()
          print(myClosure(5))
     }else{
          clearClosure()
     }
}

//Test it
foo()
foo()

//Clear it
foo(clear:true)


The other option would be to pull the closure out of foo's scope, and have another function that clears it. You could also shove all of this inside an object, and then when the object gets dealocated, everything inside gets deallocated.


A strong reference cycle is when you have objects pointing to one another in a circle, holding onto each other, so none of them gets removed. This is a global, which is defined to have a lifetime equal to the program itself. They both cause the memory to stick around, but for different reasons.

Thanks, this will work, but this approach will still not solve "return the closure and clear it" problem.


If you want to return the closure and attempt to clear it afterward, you have to use a mid-layer wrapper, which is the same as you wrote "shove all of this inside an object" and manually clear it via the wrapper.

However just as I said in the first post. I think it is somehow not elegant.


One last thing I'd like to discuss is that although func foo() is defined globally, the calling context may not be global. For example, there is an object of a certain class which needs a function to do some calculation, in the lifetime of this object, it may generate a closure using foo(), returned along with the closure is some additional data just like Nested; when this object is dead, it no longer needs this closure and that addition data. There should be a more elegant way to perform this task without having to involve a mid-layer wrapper.

If there is any more context to the actual problem you are solving that you could provide, I think I could be more helpful.


Returning the closure doesn't actually change the problem. The closure will just have an additional reference holding it, and if foo is cleared, it would be free to be released whenever you stop holding it elsewhere.


I think the main issue you are running into here is the use of globals. The solution feels off because foo() is storing state for its client, and that is going to break as soon as you have more than one client (or more than one type of client as well).


There are several possible solutions, but it is hard to know which one is correct without knowing why you need the recursion or what kind of recursion you need. For example, in some cases, you could pre-calculate the old value and then add that value to the new closure... giving you a recursive answer without actually holding on to the old closure. Or, you might want some sort of factory object, which provides closures as needed. Or you might pass the current closure to foo instead of storing it within foo. Hard to say without more info...

"Returning the closure doesn't actually change the problem. The closure will just have an additional reference holding it, and if foo is cleared, it would be free to be released whenever you stop holding it elsewhere."


Try this in your code, and you'll know it is not true.🙂

Quote: "If you want to return the closure and attempt to clear it afterward, you have to use a mid-layer wrapper, which is the same as you wrote "shove all of this inside an object" and manually clear it via the wrapper."


Oh, what I meant was that instead of having a global func foo, you would have a class/struct foo.

class foo {
     private var myClosure = { value in value }

     private func setClosure() {...}
     public func clearClosure() {...} //This is no longer required unless you want to reset the closure periodically
     
     public func doSomething()->ClosureType {
          setClosure()
          print(myClosure(5))
          return myClosure
     } 
}


What this gives you is that you can have many different instances of foo, each holding their own state (i.e. level of recursion), and the closures will all get released when that instance of foo is released (unless there are other references to them)

Well, as I have been repeating, from the start of this thread, I said I know the solution just like you posted.


The thing is apple doesn't support weak attribute for closures. If I could write code like this...

myClosure = {[weak myClosure]
        input in
        date.print()
        return input <= 1 ? 1 : input + myClosure(input - 1)
    }


Anyway, thanks for discussion.

I don't think there is any way to get around this without adding a level of scope, but it doesn't seem too bad just to make the recursive closure different from the closure you're holding a reference to.


class Nested
{
    func talk(a: String) {print("Nested \(a)")}

    deinit {print("Nested dealloc")}
}

typealias ClosureType = Int -> Int

func foo()
{
    var myClosure : ClosureType = {value in value}

    func setClosure()
    {
        var data = Nested()

        myClosure =
        {
            input in
            data.talk("outer")
         
            func recursive(input: Int, data: Nested) -> Int
            {
                data.talk("inner")
                return input <= 1 ? 1 : input + recursive(input - 1, data: data)
            }
            return recursive(input, data: data)
        }
    }

    // do something
    setClosure()
    print(myClosure(5))
}


// test it
foo()


I used a func for the recursive closure, but you should be able do it as a plain closure with a weak reference to the data as well.

Strong Reference Cycle in Recursive Closures
 
 
Q