Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference type inside value type

I am exploring Swift value types particularly structs to get a better understanding of it's uses in different scenario. I was amazed to see how enum can be used to build Binary Search Tree using indirect which introduces a thin layer of reference semantics.

enum BinarySearchTree<T: Comparable> {
  case empty
  case leaf(T)
  indirect case node(BinarySearchTree, T, BinarySearchTree)
}

Now coming to real question, what I am struggling to find is, what will happen to reference type inside a value type. How will the relationship work? like memory management, Object lifecycle.

For e.g.

class B {
    var data: Int = 0

    deinit {
        print("deallocated!")
    }

}

struct A {
    var b = B()
}

In the above case, a value type holds a reference to a reference type.

  1. When will deinit will get called?
  2. Does every new struct instance of type A will have reference to same instance of class B or will they be different.
  3. What should I need to take care of or it is a code smell?
  4. Anything else?
like image 278
Rahul Avatar asked Jan 05 '23 05:01

Rahul


2 Answers

Every struct A copy will share the same reference to B. Every new struct A, created from scratch, will hold a completely new B object.

The B.deint will be called when there are zero strong references to it (e.g., your var b is one of these strong references). For instance, if only A values hold references to a given B object then those will need to got out of scope to zero all references to this object (or their boxed copies be deallocated as well, but this might be a topic for another question.)

Code Design. If these all sounds too confusing and is blocking your app progress (with no real practical benefit so far), you might consider refactoring B to a struct as well. For instance, even Apple recommends considering value types to design your model layer. This blog post might also help make up your mind.

like image 189
Paulo Mattos Avatar answered Feb 05 '23 05:02

Paulo Mattos


You can test this in a playground:

class B {
    var data: Int = 0

    deinit {
        print("deallocated!")
    }
}

struct A {
    var b = B()
}

var a1: A? = A()
var a2: A? = A()
var a3: A? = a1

// Do the two instances of struct A share the same instance of class B?
a1?.b === a2?.b // false

// Do copies of instances of struct A share the same instance of class B?
a1?.b === a3?.b // true

// When will deinit be called?
a1 = nil    // Not yet, a3 still holds a strong reference to the shared instance of class B
a3 = nil    // Now! There are no longer any strong references to the shared instance of class B, so it is deallocated.
like image 42
paulvs Avatar answered Feb 05 '23 05:02

paulvs