Is deinit called when init throws an exception? - swift

Consider the following example class:
class SocketWrapper {
let sock: Int32
init() throws {
try sock = SocketWrapper.createSocket()
}
deinit {
close(sock)
}
}
What happens if createSocket() throws and the init() therefore fails? sock would be left uninitialized. Is deinit still called (on an partially uninitialized object) when init() throws?

deinit is not called on instances that have not been correctly initialized.
If init fails for some reason, then the class instance never starts existing. Therefore, there is no instance on which deinit could be called.
If deinit could be called on a partially initialized instance, it would break the contract of non-optional properties - in your example the socket property would not get assigned and it would still be accessible in deinit as a non-optional but without a value.

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.

Actor ‘self’ can only be passed ‘inout’ from an async initializer

In Swift, let’s say we have an actor, which has a private struct.
We want this actor to be reactive, and to give access to a publisher that publishes a specific field of the private struct.
The following code seems to work. But it produces a warning I do not understand.
public actor MyActor {
private struct MyStruct {
var publicField: String
}
#Published
private var myStruct: MyStruct?
/* We force a non isolated so the property is still accessible from other contexts w/o await in other modules. */
public nonisolated let publicFieldPublisher: AnyPublisher<String?, Never>
init() {
self.publicFieldPublisher = _myStruct.projectedValue.map{ $0?.publicField }.eraseToAnyPublisher()
}
}
The warning:
Actor 'self' can only be passed 'inout' from an async initializer
What does that mean? Is it possible to get rid of this warning? Is it “dangerous” (can this cause issues later)?
Note:
If I use $myStruct instead of _myStruct.projectedValue, it does not compile at all. I think it’s related, but I don’t truly see how.
The error is in that case:
'self' used in property access '$myStruct' before all stored properties are initialized
The warning is because when you are using _myStruct.projectedValue compiler notes that you are trying to do mutation on the actor from a synchronous context. All you have to do to remove the warning is to make your initializer asynchronous using async specifier.
The actor initializer proposal goes into more detail for why initializer needs to be asynchronous if there mutations inside actor initializer. Cosider the following case:
actor Clicker {
var count: Int
func click() { self.count += 1 }
init(bad: Void) {
self.count = 0
// no actor hop happens, because non-async init.
Task { await self.click() }
self.click() // 💥 this mutation races with the task!
print(self.count) // 💥 Can print 1 or 2!
}
}
Since there are two click method call one from Task and one called directly, there might be a case that the Task call executed first and hence the next call need to wait until that finishes.

How do I initialize a global variable with #MainActor?

I would like to have some sort of global variable that is synchronized using #MainActor.
Here's an example struct:
#MainActor
struct Foo {}
I'd like to have a global variable something like this:
let foo = Foo()
However, this does not compile and errors with Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context.
Fair enough. I've tried to construct it on the main thread like this:
let foo = DispatchQueue.main.sync {
Foo()
}
This compiles! However, it crashes with EXC_BAD_INSTRUCTION, because DispatchQueue.main.sync cannot be run on the main thread.
I also tried to create a wrapper function like:
func syncMain<T>(_ closure: () -> T) -> T {
if Thread.isMainThread {
return closure()
} else {
return DispatchQueue.main.sync(execute: closure)
}
}
and use
let foo = syncMain {
Foo()
}
But the compiler does not recognize if Thread.isMainThread and throws the same error message again, Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context.
What's the right way to do this? I need some kind of global variable that I can initialize before my application boots.
One way would be to store the variable within a container (like an enum acting as an abstract namespace) and also isolating this to the main actor.
#MainActor
enum Globals {
static var foo = Foo()
}
An equally valid way would be to have a "singleton-like" static property on the object itself, which serves the same purpose but without the additional object.
#MainActor
struct Foo {
static var shared = Foo()
}
You now access the global object via Foo.global.
One thing to note is that this will now be lazily initialized (on the first invocation) rather than immediately initialized.
You can however force an initialization early on by making any access to the object.
// somewhere early on
_ = Foo.shared
Bug in Swift 5.5, 5.6, 5.7
TL;DR: #MainActor sometimes won't call static let variables on the main thread. Use private(set) static var instead.
While this compiles and works, it appears that this may call the initializer off the main thread and subsequently any calls made inside the initializer.
#MainActor
func bar() {
print("bar is main", Thread.isMainThread)
}
#MainActor
struct Foo {
#MainActor
static let shared = Foo()
init() {
print("foo init is main", Thread.isMainThread)
bar()
}
func fooCall() {
print("foo call is main", Thread.isMainThread)
}
}
Task.detached {
await Foo.shared.fooCall()
}
// prints:
// foo init is main false
// bar is main false
// foo call is main true
This is a bug (see issue 58270).
A workaround is to always use a private(set) static var instead of a static let, as the correct isolation behaviour is enforced in that case.

CLKComplicationServer initializes an already initialized singleton class CLKComplicationDataSource, although init() is private

To update complications on the watch, I use a singleton class ComplicationController (irrelevant code has been omitted below):
final class ComplicationController: NSObject, CLKComplicationDataSource {
static let shared = ComplicationController() // Instantiate the singleton
private override init() {
super.init()
print("====self: \(self): init")
} // Others can't init the singleton
}
The singleton is created on the watch by the extension delegate:
class ExtensionDelegate: NSObject, WKExtensionDelegate {
override init() {
super.init()
_ = ComplicationController.shared
}
}
When I launch the watch extension with a breakpoint at the print statement above, the execution breaks and the stack trace is:
When I then execute the print statement in a single step, the debugger shows:
====self: <Watch_Extension.ComplicationController: 0x7bf35f20>: init
When I then continue, the execution breaks again at the same breakpoint, and the stack trace is:
After another single step, the debugger shows:
====self: <Watch_Extension.ComplicationController: 0x7d3211d0>: init
Obviously, the CLKComplicationServer has created another instance of the singleton.
My question is: Did I something wrong or is this a bug? If it is a bug, is there a workaround?
PS: It does not help not to initialize ComplicationController in the ExtensionDelegate. In this case, the 2nd instance is created as soon as ComplicationController.shared is used anywhere in the code.
I found a workaround:
Do not instantiate the singleton in the app, i.e. do not use ComplicationController.shared anywhere.
If you have to call functions in ComplicationController.shared, send a notification, e.g. to the default notification center.
The ComplicationController singleton had to have registered for such notifications, and when it receives one, it has to execute the required function.
This did work for me.

Understanding optional global variables in swift

I'm working through a book on Swift and I understand the idea of scope in functions so what I'd like to understand next is why we set global variables using optional types in classes. Honestly it looks like we don't set these variables per say but just let the class know that there will be a variable of a specific type somewhere throughout the code base: var sut: ItemManager!.
From what I understand the variable sut is an unwrapped optional of the type ItemManger which definitely has a valid value and not nil. The reason we've set it with an exclamation mark or question mark is because there isn't an initializer within this class. What isn't clear is since this class doesn't have an initializer, what factors come into play when deciding weather to set this global variable to an optional using a questions mark or implicitly unwrapped with an exclamation mark?
import XCTest
#testable import ToDo
class ItemManagerTests: XCTestCase {
var sut: ItemManager!
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
sut = ItemManager.init()
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func test_ToDoCount_InitiallySetAtZero(){
let sut = ItemManager.init()
XCTAssertEqual(sut.toDoCount, 0)
}
func test_DoneCount_InitiallySetAtZero(){
let sut = ItemManager.init()
XCTAssertEqual(sut.doneCount, 0)
}
}
sut is not a global variable.
sut is an instance variable of the ItemManagerTests. Every time a new instance, i.e. an object, of a ItemManagerTests is instantiated, memory is allocated for sut.
It's of type ItemManager!. This is an implicitly unwrapped optional. It's similar to ItemManager? (a.k.a. Optional), with the distinction that it is implicitly forcefully unwrapped everywhere it's used directly, as if the force unwrap operator (!) was used.
Its value is initialized to nil, but is set to a new ItemManager object when setUp is called by Xcode's testing framework.
Firstly, sut is not a global. A global variable in Swift is declared outside any enclosing scope (So, before the class statement). sut is an instance property; each instance of your ItemManagerTests class will have a sut property.
In Swift, all non-optional properties must be initialised by the time the initialiser has completed. In some cases this can be achieved by code inside the initialiser and in other cases by assigning a default value.
In some cases, s you cannot assign or don't want to assign a default value and you can't (or don't want to) override the initialiser.
If you used a normal optional then the compiler would be satisfied that the property wasn't initialised by default or in the initialiser, but each time you referred to the property you would have to unwrap it (e.g. sut?.donecount).
Since your test case is assigning a value to sut in setup, you know that it will have a value and you could use a force unwrap (e.g. sut!.donecount), or, take it one step further and use an implicitly unwrapped optional. This allows you to refer to the property without any unwrapping, but it is still an optional and will still cause a crash if it is nil.
Note that your current code isn't even using the property, since you are allocating a local variable in your test cases. You can use:
class ItemManagerTests: XCTestCase {
var sut: ItemManager!
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
sut = ItemManager.init()
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func test_ToDoCount_InitiallySetAtZero() {
XCTAssertEqual(sut.toDoCount, 0)
}
func test_DoneCount_InitiallySetAtZero() {
XCTAssertEqual(sut.doneCount, 0)
}
}
The reason we've set it with an exclamation mark or question mark is
because there isn't an initializer within this class
Because when every instance of a class is initialized, it has to allocate memory for its properties/instance var. So with var sut: ItemManager!, the value of sut is nil when init. Without !, ? the compiler can't allocate, init, so you have to init manually in an initializer. That is what compiler told you.
For using ! or ?.
! is used when the property/var always has value after the first assigning. And after the first time it's assigned, it can't be nil later
? is used when the pro/var can have value or not