Does setting an optional instance to nil call deinit (if implemented)? [duplicate] - swift

In the next code I'm trying to call the deinit method releasing all the references to the Person Class instance Mark but the deinit is never called. Why?
class Person{
let name:String
init(name:String){
self.name = name
println("Person created")
}
deinit {
println("Person \(name) deinit")
}
}
var Mark:Person? = Person(name:"Mark")
Mark = nil // Shouldn't the person deinit method be called here? It doesn't.

Xcode's Playgrounds for Swift don't work like regular apps; they aren't being run just once. The objects created stay in memory and can be inspected until you change the code, at which point the whole playground is reevaluated. When this happens, all previous results are discarded and while all object will be deallocated, you won't see any output from that.
Your code is correct, but Playgrounds is not suited to test things related to memory management.
Here's a related SO question: Memory leaks in the swift playground / deinit{} not called consistently

Deinit will called if create object like this
_ = Person(name:"Mark")

Deinit gets called when you ignore the variable like so.
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
_ = Owner()
PlaygroundPage.current.finishExecution()
Owner class -
public class Owner {
public var car: Car?
public init (_ car: Car? = nil) {
self.car = car
print ("Owner got allocated")
}
deinit {
print ("owner got deallocated")
}
}
// Prints -
Owner got allocated
owner got deallocated

workaround - move all variable initialisation code into a function and call that function.

Playground having issues used to be a problem. For 99% of the memory management cases they work just like a normal project. Playground has improved A LOT over time.
Such a problem should no longer exist and Playground can be used reliably.

Related

Is deinit Guaranteed to be Called When the Program Finishes?

I have the following code:
class Problem{
init(){
print("Problem init");
}
deinit{
print("Problem deinit");
}
}
var list = Problem();
The output:
Problem init
The following causes the program to call deinit:
class Problem{
init(){
print("Problem init");
}
deinit{
print("Problem deinit");
}
}
do {
var list = Problem();
}
Questions:
Why isn't deinit called the first time?
Is there a way to guarantee that deinit will always be called for Problem in code that I have not control of how it is written(i.e., user code)?
P.S. I know there is most likely an obvious reason that I, as a programmer that is new to Swift, have overlooked.
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.

Implicitly unwrapped optional var destroyed by compiler before end of scope?

With swift compiler optimizations implicitly unwrapped optional variables do not survive the whole scope, but are released immediately after usage.
Here is my environment:
swift --version
outputs
Apple Swift version 5.3.2 (swiftlang-1200.0.45 clang-1200.0.32.28)
Target: x86_64-apple-darwin20.2.0
Xcode version is Version 12.3 (12C33)
Consider this most rudimentary example that shows the issue:
final class SomeClass {
func doSth() {}
deinit {
print("deinit")
}
}
func do() {
var someObject: SomeClass! = SomeClass()
someObject.doSth()
print("done")
}
This should ouput
done
deinit
However, in release builds (with Swift code optimizations enabled "-O") it prints the other way round:
deinit
done
This is ONLY the case for var someObject: SomeClass!.
The following alterations of that code ALL output correctly (meaning the Object is released when the scope of the function is left):
Define var as constant:
func doSthSpecial() {
let someObject: SomeClass! = SomeClass()
someObject.doSth()
print("done")
}
Define var as optional explicitly:
func doSthSpecial() {
var someObject: SomeClass? = SomeClass()
someObject.doSth()
print("done")
}
Access like an optional:
func doSthSpecial() {
var someObject: SomeClass! = SomeClass()
someObject?.doSth()
print("done")
}
These last three implementations all output
done
deinit
in that order.
Somehow this leaves me speechless 🤷‍♂️.
I understand this optimization, it makes sense. But as a programmer we are used to local variables inside of functions being available until leaving the scope.
The problem I have here is about the lifetime of an object that is stored in such an implicitly unwrapped optional variable. If I have code that depends on the lifetime of this object (which is the case with RxSwift and its DisposeBags for example) then I am getting weird behavior, unexpected behavior!
I could consider this as a bug in Swift, but what do you think? Bug or no bug?
Here is a more real-world scenario with RxSwift where you could be using such a construct:
import UIKit
import RxSwift
final class SomeClass {
func doSth() {}
deinit {
print("deinit")
}
}
class ViewController: UIViewController {
let providePassword = PublishSubject<String>()
lazy var askForPassword: Observable<String> = {
return Observable.create { observer in
_ = self.providePassword.subscribe(observer)
return Disposables.create()
}
.debug(">>> ask for password signal")
}()
private func performAsyncSyncTask() {
DispatchQueue.global().async {
var disposeBag: DisposeBag! = DisposeBag()
let sema = DispatchSemaphore(value: 0)
self.askForPassword
.subscribe(onNext: { pw in
print(pw)
sema.signal()
})
.disposed(by: disposeBag)
_ = sema.wait(timeout: DispatchTime.distantFuture)
disposeBag = nil
}
}
#IBAction func startAskPassword(sender: AnyObject) {
self.performAsyncSyncTask()
}
#IBAction func sendPassword(sender: AnyObject) {
self.providePassword.on(.next("hardcoded pw"))
}
}
The problem here is: When executing self.performAsyncSyncTask() it is subscribed to askForPassword but because in optimized builds the implicitly unwrapped optional variable is purged immediately after using it in .disposed(by: disposeBag).
This destroys the signal immediately after subscribing to it.
But as a programmer we are used to local variables inside of functions being available until leaving the scope.
This hasn't been the case since ARC was first released for ObjC. ARC has always had the option to release objects after their last use (and very often makes use of this). This is by design, and is not a bug in Swift (or in ObjC, where it's also true).
In Swift, if you want to extend the lifetime of an object beyond its last use,withExtendedLifetime is explicitly for this purpose.
var someObject: SomeClass! = SomeClass()
withExtendedLifetime(someObject) {
someObject.doSth()
print("done")
}
Keep in mind that it is legal for objects to have balanced retain/autorelease calls on them, which may cause them to outlive their scope as well. This is much less common in Swift, but still legal and happens if you pass a Swift object to ObjC (which can happen in many places you may not expect).
You should be very careful relying on when deinit will be called. It can surprise you, and isn't even promised in all cases (for example, deinit is not called during program quit on Mac, which tends to surprise C++ developers).
IMO performAsyncSyncTask is a dangerous pattern, and should be redesigned with clearer ownership. I don't do enough RxSwift work to immediately redesign it, but blocking the whole thread on a DispatchSemaphore seems the wrong way to integrate with any reactive system. Threads are a finite resource, and this forces the system to create more while this one is blocked doing nothing.

Swift 4 retain cycle different from swift 3? [duplicate]

In the next code I'm trying to call the deinit method releasing all the references to the Person Class instance Mark but the deinit is never called. Why?
class Person{
let name:String
init(name:String){
self.name = name
println("Person created")
}
deinit {
println("Person \(name) deinit")
}
}
var Mark:Person? = Person(name:"Mark")
Mark = nil // Shouldn't the person deinit method be called here? It doesn't.
Xcode's Playgrounds for Swift don't work like regular apps; they aren't being run just once. The objects created stay in memory and can be inspected until you change the code, at which point the whole playground is reevaluated. When this happens, all previous results are discarded and while all object will be deallocated, you won't see any output from that.
Your code is correct, but Playgrounds is not suited to test things related to memory management.
Here's a related SO question: Memory leaks in the swift playground / deinit{} not called consistently
Deinit will called if create object like this
_ = Person(name:"Mark")
Deinit gets called when you ignore the variable like so.
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
_ = Owner()
PlaygroundPage.current.finishExecution()
Owner class -
public class Owner {
public var car: Car?
public init (_ car: Car? = nil) {
self.car = car
print ("Owner got allocated")
}
deinit {
print ("owner got deallocated")
}
}
// Prints -
Owner got allocated
owner got deallocated
workaround - move all variable initialisation code into a function and call that function.
Playground having issues used to be a problem. For 99% of the memory management cases they work just like a normal project. Playground has improved A LOT over time.
Such a problem should no longer exist and Playground can be used reliably.

Swift: Self.init called multiple times in initializer

This one has me stumped. I can't figure out why Swift is complaining that self.init is called more than once in this code:
public init(body: String) {
let parser = Gravl.Parser()
if let node = parser.parse(body) {
super.init(document: self, gravlNode: node)
} else {
// Swift complains with the mentioned error on this line (it doesn't matter what "whatever" is):
super.init(document: self, gravlNode: whatever)
}
}
Unless I'm missing something, it's very obvious that it is only calling init once. Funnily enough if I comment out the second line Swift complains that Super.init isn't called on all paths, lol.
What am I missing?
Update:
Ok so the problem was definitely trying to pass self in the call to super.init. I totally forgot I was doing that, ha. I think I had written that experimentally and gotten it to compile and thought that it might actually work, but looks like it's actually a bug that it compiled that way at all.
Anyhow, since passing self to an initializer is kind of redundant since it's the same object, I changed the parent initializer to accept an optional document parameter (it's just an internal initializer so no big deal) and if it's nil I just set it to self in the parent initializer.
For those curious, this is what the parent initializer (now) looks like:
internal init(document: Document?, gravlNode: Gravl.Node) {
self.value = gravlNode.value
super.init()
self.document = document ?? self as! Document
// other stuff...
}
I suspect this is a bad diagnostic (i.e the wrong error message). It would be very helpful if you had a full example we could experiment with, but this line doesn't make sense (and I suspect is the underlying problem):
super.init(document: self, gravlNode: node)
You can't pass self to super.init. You're not initialized yet (you're not initialized until you've called super.init). For example, consider the following simplified code:
class S {
init(document: AnyObject) {}
}
class C: S {
public init() {
super.init(document: self)
}
}
This leads to error: 'self' used before super.init call which I believe is the correct error.
EDIT: I believe Hamish has definitely uncovered a compiler bug. You can exploit it this way in Xcode 8.3.1 (haven't tested on 8.3.2 yet):
class S {
var x: Int
init(document: S) {
self.x = document.x
}
}
class C: S {
public init() {
super.init(document: self)
}
}
let c = C() // malloc: *** error for object 0x600000031244: Invalid pointer dequeued from free list

Deinit method is never called - Swift playground

In the next code I'm trying to call the deinit method releasing all the references to the Person Class instance Mark but the deinit is never called. Why?
class Person{
let name:String
init(name:String){
self.name = name
println("Person created")
}
deinit {
println("Person \(name) deinit")
}
}
var Mark:Person? = Person(name:"Mark")
Mark = nil // Shouldn't the person deinit method be called here? It doesn't.
Xcode's Playgrounds for Swift don't work like regular apps; they aren't being run just once. The objects created stay in memory and can be inspected until you change the code, at which point the whole playground is reevaluated. When this happens, all previous results are discarded and while all object will be deallocated, you won't see any output from that.
Your code is correct, but Playgrounds is not suited to test things related to memory management.
Here's a related SO question: Memory leaks in the swift playground / deinit{} not called consistently
Deinit will called if create object like this
_ = Person(name:"Mark")
Deinit gets called when you ignore the variable like so.
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
_ = Owner()
PlaygroundPage.current.finishExecution()
Owner class -
public class Owner {
public var car: Car?
public init (_ car: Car? = nil) {
self.car = car
print ("Owner got allocated")
}
deinit {
print ("owner got deallocated")
}
}
// Prints -
Owner got allocated
owner got deallocated
workaround - move all variable initialisation code into a function and call that function.
Playground having issues used to be a problem. For 99% of the memory management cases they work just like a normal project. Playground has improved A LOT over time.
Such a problem should no longer exist and Playground can be used reliably.