2

Can someone please help me to avoid memory leak below

class Base {
     var refer: Referenced?
    init() {
        self.refer = Referenced()
    }
    deinit {
        print("deinit called")
    }
}



class Referenced {
    var native: Native
    init(){
       native = Native.init(baseRef: Base())
    }


}
struct Native {
    var baseRef: Base
}
func testMe () {
    var base:Base? = Base()
    base?.refer = nil
    base = nil
}

testMe()

Looks like there is a cyclic reference in here. Base -> Referenced and Referenced -> Native -> Base

But I am unsure how to break this cycle .

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • 2
    memory leak is a secondary issue. You have infinite circular dependency between `Base` and `Referenced` classes. – Kamran Jun 12 '19 at 11:37
  • Possible duplicate of [Shall we always use \[unowned self\] inside closure in Swift](https://stackoverflow.com/questions/24320347/shall-we-always-use-unowned-self-inside-closure-in-swift) – Mohmmad S Jun 12 '19 at 12:01

4 Answers4

2

There are two issues here:

  1. As others have pointed out, in general, when you have two or more reference types referring to each other, you need to make one of those references weak to break the strong reference cycle. See Resolving Strong Reference Cycles Between Class Instances.

  2. The more serious problem here is that you have infinite loop. The init of Base is instantiating a new Referenced instance, but the init of Referenced is creating another Base instance (which it’s passing to the Native instance). That new Base instance will then create another Referenced instance, repeating the process, and it will continue doing this until it runs out of memory/stack.

    Add print statements inside your various init methods, and you’ll see this infinite loop in action.

You should revisit this model, identify an “ownership” graph. Parent objects can keep strong references to child objects (and possibly instantiate child objects, if appropriate), but child objects should only keep weak references back to their parent objects (and most likely not instantiate new parent objects).

For example, you might do:

class Parent {
    var child: Child?

    init() {
        self.child = Child(parent: self)
    }

    deinit {
        print("deinit called")
    }
}

class Child {
    weak var parent: Parent?

    init(parent: Parent) {
        self.parent = parent
    }
}

And

func testMe() {
    var parent: Parent? = Parent()
    parent = nil                   // in this case, this is unnecessary because `parent` is a local variable, but this is here for illustrative purposes; but note that we don’t have to `nil` the `child`
}

Here, you instantiate a Parent object, which happens to instantiate a child. But note that:

  1. The Parent supplies itself as a parameter to the Child;

  2. The Child only maintains a weak reference back to the Parent; and

  3. The Child obviously does not instantiate a new Parent, but rather just uses the reference that was supplied to it.

You can do a rendition of this with your Native struct, too, but the idea will be the same: Break the strong reference cycle with a weak reference and avoid the infinite loop.


Frankly, in these cases, abstract type names make examples unnecessarily confusing. If you clarify what these various types actual represent with a practical, real-world example, the ownership model will undoubtedly become more self-evident.

Community
  • 1
  • 1
Rob
  • 415,655
  • 72
  • 787
  • 1,044
0

Change one of the reference as weak like this :

class Base {
    weak var refer: Referenced?
    init() {
        self.refer = Referenced()
    }
    deinit {
        print("deinit called")
    }
}

Hope this helps.

BhargavR
  • 1,095
  • 7
  • 14
  • If you make `refer` a `weak` property, then `self.refer = Referenced()` will be deallocated immediately. You’re right that `weak` references can break strong reference cycles, but the example here won’t work. – Rob Jun 12 '19 at 14:57
0

You need to mark either Base.refer or Native.baseRef as weak to break the cycle, i.e. change var to weak var.

fphilipe
  • 9,739
  • 1
  • 40
  • 52
0

First of all, at the code snippet you passed, you have a problem with infinite circular dependency like this:

Base -> Referenced -> Native -> Base...

An instance of Native struct is not referencing the Base instance at the beginning of this chain, it is always creating a new one and this behavior is infinite. Just copy your code to the playground and try to run it. You will receive an "Execution was interrupted" error. So, your code won't even run. As I suppose, what you are trying to achieve is for a Native struct to hold the reference to the first Base instance. Here is the right solution where we pass the reference from Base to Referenced and to Native that will hold the reference:

class Base {
    var refer: Referenced?
    init() {
        self.refer = Referenced(base: self)
    }
    deinit {
        print("deinit called")
    }
}

class Referenced {
    var native: Native
    init(base: Base){
        native = Native.init(baseRef: base)
    }
}

struct Native {
    var baseRef: Base
}

And now, as Native is referencing to the Base parent, you will struggle with the memory leak you mention in the original question. And this time, to prevent the memory leak you have to make your baseRef var weak like this:

weak var baseRef: Base?