When Should I Use Deinit

When should I use deinit?

It's not required that you implement that method, but you can use it if you need to do some action or cleanup before deallocating the object.

The Apple docs include an example:

struct Bank {
static var coinsInBank = 10_000
static func vendCoins(var numberOfCoinsToVend: Int) -> Int {
numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank)
coinsInBank -= numberOfCoinsToVend
return numberOfCoinsToVend
}
static func receiveCoins(coins: Int) {
coinsInBank += coins
}
}

class Player {
var coinsInPurse: Int
init(coins: Int) {
coinsInPurse = Bank.vendCoins(coins)
}
func winCoins(coins: Int) {
coinsInPurse += Bank.vendCoins(coins)
}
deinit {
Bank.receiveCoins(coinsInPurse)
}
}

So whenever the player is removed from the game, its coins are returned to the bank.

When is `deinit` exactly called? (in Swift)

The deinit is intended to release resources (such as freeing memory that is not under ARC).

(Thanks to Martin
and Rob's input, we can conclude below)

When is deinit called?

Usually, when last strong-reference gets out of scope,
deinit is called instantly (then deallocation happens).

  • But if affected by autorelease feature, deinit is called significantly later,
    long after last reference gets out of scope (when autorelease pool is drained).
  • And when App is terminating, deinit is guaranteed to never get called!?
    (if deinit was not already called).
  • Also in extremely common cases, deinit is called before strong-ref-variable's scope ends:
    • In Swift unlike other languages, when we set a weak-reference equal to a strong-reference,
      it could result to nil (which is absolutely allowed by Swift).

    • This happens if compiler detects that the remaining lines of scope,
      have NOT any strong-reference.

    • Possible workaround is using withExtendedLifetime(_:_:) global-method, like:

    withExtendedLifetime(myStrongRefVariable) {
    // Do something that only needs non-nil weak reference.
    }

Is it like C++ destructor?

There is no equivalent of a C++ destructor in ObjC or Swift.

(Objective-C++ object's destructor are called during program termination,
since that is required by C++ spec,
but that's all and Obj-C++'s dealloc behavior is same as deinit.)

Is Swift using Garbage-Collector?

No, but whenever autorelease feature affects objects,
the deinit can be postponed (till autorelease-pool is drained, as mentioned above).

Deinit is it a good practice to implement it on viewControllers?

By default, you don't have to implement the deinit method in your classes:

Swift automatically deallocates your instances when they are no longer
needed, to free up resources. Swift handles the memory management of
instances through automatic reference counting (ARC), as described in
Automatic Reference Counting. Typically you don’t need to perform
manual cleanup when your instances are deallocated. However, when you
are working with your own resources, you might need to perform some
additional cleanup yourself. For example, if you create a custom class
to open a file and write some data to it, you might need to close the
file before the class instance is deallocated.

Swift Deinitialization Documentation - How Deinitialization Works Section.

Usually, when working with View Controllers it seems that there is no need to do such an implementation. However, as mentioned in @rmaddy's comment, it is still an approach for tracing memory leak or reference cycle with the view controller.

If your purpose is to check if the controller has been removed from the hierarchy (view controller life cycle), you could implement viewWillDisappear(_:) or viewDidDisappear(_:) methods; Note that calling on of these methods does not guarantees that the deinit will be called, i.e it does not mean that disappearing the view controller always leads to deallocate it (related: Deinit never called, explanation for deinit not called).

Also:

these Q&As should be useful:

  • When should I use deinit?

  • how to make deinit take effect in swift

  • Understand deinitialization and inheritance in swift language

how to make deinit take effect in swift


class Car {
static var population: Int = 0
init() {
Car.population += 1

}
deinit {
Car.population -= 1
}
}

var cars: [Car] = [Car(), Car()]
print("Population:", Car.population) // "Population: 2"

// now the second car is removed from array and we have no other references to it
// it gets removed from memory and deinit is called
cars.removeLast()
print("Population:", Car.population) // "Population: 1"

However, the same can be achieved just by asking the number of items in cars array. And that's usually a better alternative than a private counter of instances.

To keep the items in memory you will always need some kind of register (e.g. an array) for them. And that register can keep them counted.

One possibility:

class CarPopulation {
var liveCars: [Car] = []
var junkCars: [Car] = []
}

Or you can keep them in one array and set junk on the car and count non-junk cars when needed:

class CarPopulation {
var cars: [Car] = []

func liveCars() -> Int {
return self.cars.filter { !$0.junk }.count
}
}

There are many possibilities but extracting the counters to some other class that owns the cars is probably a better solution.

Should deinit be overridden to remove observers in Swift?

As of iOS 9 , you don't need to remove observers yourself, if you're not using block based observers though. The system will do it for you, since it uses zeroing-weak references for observers, where it can.

If the observer is able to be stored as a zeroing-weak reference the
underlying storage will store the observer as a zeroing weak
reference, alternatively if the object cannot be stored weakly (i.e.
it has a custom retain/release mechanism that would prevent the
runtime from being able to store the object weakly) it will store the
object as a non-weak zeroing reference. This means that observers are
not required to un-register in their deallocation method.

Block based observers via the -[NSNotificationCenter
addObserverForName: object: queue: usingBlock] method still need to be
un-registered when no longer in use since the system still holds a
strong reference to these observers.

Apple Docs

and for super.deinit() apple says

Deinitializers are called automatically, just before instance
deallocation takes place. You are not allowed to call a deinitializer
yourself. Superclass deinitializers are inherited by their subclasses,
and the superclass deinitializer is called automatically at the end of
a subclass deinitializer implementation. Superclass deinitializers are
always called, even if a subclass does not provide its own
deinitializer.

swift docs

Is deinit Guaranteed to be Called When the Program Finishes?

It is because of the difference in Scopes between these two example that you create by adding the do-block.

In the first scenario, when that code is ran, an instance of Problem is created (initialized) at a Global Scope (outside of a class or struct definition in Swift) and then it just sits there. The program does not end and it is never de-initialized.

In the second scenario, you create the instance of Problem inside a the do-block, so it's scope is limited to inside that block. When the do-block ends, the instance is dereferenced, and thus de-initialized.

Why don't structs have deinitializers in Swift like classes?

deinit is for reference types (see https://stackoverflow.com/a/27366050/341994 for what that means) where the object pointed to persists independently until a reference count drops to zero.

A struct is a value type and does not require memory management that would need a deinit. Structs are not independently persistent the way classes are. Merely setting a property of a struct destroys it and replaces it. Assigning a struct copies it. Structs are created and destroyed in a highly lightweight way. They don’t need to signal their destruction; they are too lightweight for that.

Is cleaning up strong references in deinit a correct pattern?

This statement

[...] that recommend removing an observer from the NotificationCenter in the deinit of the UIViewController [...]

was true in the past.

And your statement

[...] if there still is a strong reference to the class, deinit will not get called.

is correct.

Observers have weak reference

An observer holds a weak reference to the target object.

This explain why the deinit of an object will be called even if there are multiple active observers.

So why do we want to remove the observers in the deinit?

This was needed prior to iOS 9 to prevent an observer from invoking a method of a deallocated object.

However unregistering an observer is no longer needed from macOS 10.11 and iOS 9.0

In OS X 10.11 and iOS 9.0 NSNotificationCenter and NSDistributedNotificationCenter will no longer send notifications to registered observers that may be deallocated.

Source

Swift - self in deinit method

In 90% of cases you have to use self in deinit. Both in Swift and Objective-C.

That's actually the whole point of that method - the last chance to access that object before deallocation.

What you should avoid is storing self to another object from deinit, not accessing self.

Also, in Objective-C some people try to avoid using property setters and getters in init and dealloc to avoid dangerous side-effects that might be hidden in them, and they are accessing ivars directly instead (_prop = nil instead of self.prop = nil). That's actually impossible in Swift because there are no ivars. However, Swift is much safer in that regard. Note that _prop = nil in Objective-C still accesses self. It's just a short syntax for self->_prop = nil. We are avoiding properties, not self.



Related Topics



Leave a reply



Submit