Dealloc in Swift

dealloc in Swift


deinit {
// perform the deinitialization
}

From the Swift Documentation:

A deinitializer is called immediately before a class instance is
deallocated. You write deinitializers with the deinit keyword, similar
to how intializers are written with the init keyword. Deinitializers
are only available on class types.

Typically you don’t need to perform manual clean-up when your
instances are deallocated. However, when you are working with your own
resources, you might need to perform some additional clean-up
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.

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).

Swift: How to deallocate properties when init throw?

I found a solution to my problem.

import XCTest

class Person {

// case1
let pointer1: UnsafeMutablePointer<Int> = UnsafeMutablePointer<Int>.allocate(capacity: 1)

// case2
let pointer2: UnsafeMutablePointer<Int>

let name: String

init(name: String) throws {

// deallocate helper for case1
var deallocateHelper1: UnsafeMutablePointer<Int>? = self.pointer1
defer {
deallocateHelper1?.deallocate()
}

// case2
self.pointer2 = UnsafeMutablePointer<Int>.allocate(capacity: 1)
var deallocateHelper2: UnsafeMutablePointer<Int>? = self.pointer2
defer {
deallocateHelper2?.deallocate()
}

// case ...


if name == "UnsupportName" {
throw NSError()
}
self.name = name

// In the end. set deallocate helpers to nil
deallocateHelper1 = nil
deallocateHelper2 = nil
}

deinit {
pointer1.deallocate()
pointer2.deallocate()
}
}

class InterestTests: XCTestCase {

func testExample() {
while true {
_ = try? Person(name: "UnsupportName")
}
}

}

Swift 5 : how to deallocate memory, allocated by shared library

malloc()ed memory must be released with free():

if let dataStr = rxData {
// do something with `dataStr`
// ...
free(dataStr)
}

This is also a good use-case for defer, which calls the closure just before the scope of the block is left:

if let dataStr = rxData {
defer { free(dataStr) }
// do something with `dataStr`
// ...
}

On Apple platforms, free() is imported from the standard C library as part of the Darwin module (which is imported by Foundation, AppKit, or UIKit). On Linux you would import Glibc.

Deinit not called on a UIViewController, but Dealloc is

TLDR:

Breakpoints will only work in deinit if there is an executable line of code ahead of them.

  • If you place a breakpoint on a line of executable code, then it will work.
  • The executable line of code must belong to the deinit method.

Thanks to Adam for pointing me in the right direction. I didn't do extensive tests, but it looks like breakpoints behave differently in deinit than everywhere else in your code.

I will show you several examples where I added a breakpoint on each line number. Those that will work (e.g. pause execution or perform their action such as logging a message) will be indicated via the ➤ symbol.

Normally breakpoints are hit liberally, even if a method does nothing:

➤ 1
➤ 2 func doNothing() {
➤ 3
➤ 4 }
5

However, in a blank deinit method, NO breakpoints will ever get hit:

  1
2 deinit {
3
4 }
5

By adding more lines of code, we can see that it depends on if there is an executable line of code following the breakpoint:

➤ 1 
➤ 2 deinit {
➤ 3 //
➤ 4 doNothing()
➤ 5 //
➤ 6 foo = "abc"
7 //
8 }
9

In particular, play close attention to lines 7 and 8, since this differs significantly from how doNothing() behaved!

If you got used to this behavior of how the breakpoint on line 4 worked in doNothing(), you may incorrectly deduce that your code is not executing if you only had a breakpoint on line 5 (or even 4) in this example:

➤ 1  
➤ 2 deinit {
➤ 3 number++
4 // incrementNumber()
5 }
6

Note: for breakpoints that pause execution on the same line, they are hit in the order that they were created. To test their order, I set a breakpoint to Log Message and Automatically continue after evaluating actions.

Note: In my testing there was also another potential pitfall that might get you: If you use print("test"), it will pop up the Debug Area to show you the message (the message appears in bold). However, if you add a breakpoint and tell it to Log Message, it will log it in regular text and not pop open the Debug Area. You have to manually open up the Debug Area to see the output.

Note: This was all tested in Xcode 7.1.1

Deallocation of ViewController

You are misunderstood the deinit method's job. The deinit is supposed to be called when the instance of a view controller has no reference left to it. So, just simply removing the references of the properties of a view controller doesn't do the whole job.

And you have a misconception of making self.delegate = nil in your SecondVC. This should have been done in your first ViewController.

To make sense of everything, I've done a sample project where you can learn how deinits work. The main code goes here:

First View Controller

class FirstViewController: UIViewController, Navigator {

override func viewDidLoad() {
super.viewDidLoad()
}

deinit {
print("First view controller's deinit called")
}

func passData(data: String) {
print("In First view controller: \(data)")
}

@IBAction func gotoSecond(_ sender: UIButton) {
let viewcontroller = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
viewcontroller.delegate = self
show(viewcontroller, sender: self)
}

}



Second View Controller

protocol Navigator {
func passData(data:String)
}

class SecondViewController: UIViewController {

weak var delegate:Navigator?

override func viewDidLoad() {
super.viewDidLoad()
}

deinit {
print("Second view controller's deinit called")
}

@IBAction func closeButton(_ sender: UIButton) {
delegate?.passData(data: "Delegation from second view controller")
dismiss(animated: true, completion: nil) //when this line executes, the instance of this class is de-referenced. This makes the call to deinit method of this class.
}
}

So, when dismiss happens for second view controller, the reference count goes to 0 for second view controller and this does the job for calling deinit method of second view controller.

But you technically don't call the deinit of the first view
controller as you don't actually de-reference the first view
controller.

You can find the whole project here.

Dismissing ViewController doesn't deallocate memory in Swift

That Library seems to have an error in the use form, in the example code is declared a method like this

func picker(sender: AZAPicker<PickerItem>, item: PickerItem) {
print("didPickItem: \(item)")
}

but instead of doing that you need to assign a closure in this way and problem solved!

UPDATED (in your code)

self.pickerView!.onPickItem = {[weak self]( sender : AZAPicker<PickerItem>,item:PickerItem) in
print("didPickItem: \(item)")
self.currentPoint = item.number

}

Full Code Reference

class ViewController: UIViewController {

@IBAction func dismiss(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()

let config = AZAPickerConfiguration<PickerItem>(items: (1...100).map { PickerItem(number: $0) },
defaultSelectedIndex: 99,
selectionRadiusInPercent: 0.5,
itemWidth: 80)

let pickerView = AZAPicker<PickerItem>(with: config, frame: .zero)

pickerView.backgroundColor = .white
//in this code [weak self] is not needed because I don't use self inside the closure
pickerView.onPickItem = {( sender : AZAPicker<PickerItem>,item:PickerItem) in
print("didPickItem: \(item)")
}

pickerView.translatesAutoresizingMaskIntoConstraints = false

view.addSubview(pickerView)

NSLayoutConstraint(item: pickerView, attribute: .top, relatedBy: .equal, toItem: topLayoutGuide, attribute: .top, multiplier: 1, constant: 20).isActive = true

NSLayoutConstraint(item: pickerView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0).isActive = true

NSLayoutConstraint(item: pickerView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0).isActive = true

NSLayoutConstraint(item: pickerView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 80).isActive = true
}

}

Why won't this Alert let my view controller deallocate?

Have the YES action handler declare [weak self] and call self?.popInit().

Also, as suggested in a comment, you can replace the NO handler with nil.



Related Topics



Leave a reply



Submit