Stacktraces and deinitializers in Swift
While continuing to make my way through
the Swift Book, I finally reached
the definition of Deinitialization
. The authors wait until almost
halfway through the pagecount before introducing this concept. Since
this is the first time in the book a code sample has code that is called
by the runtime rather than the reader, I wanted to print a stack trace
in the deinitializer and see what it looked like.
Here is the Deinitialization
example from the Swift Book with my call
to print the stack trace on line 25. I had to add the import
Foundation
to make it compile with that line in there.
- import Foundation
- class Bank {
- static var coinsInBank = 10_000
- static func distribute(coins numberOfCoinsRequested: Int) -> Int {
- let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)
- coinsInBank -= numberOfCoinsToVend
- return numberOfCoinsToVend
- }
- static func receive(coins: Int) {
- coinsInBank += coins
- }
- }
- class Player {
- var coinsInPurse: Int
- init(coins: Int) {
- coinsInPurse = Bank.distribute(coins: coins)
- }
- func win(coins: Int) {
- coinsInPurse += Bank.distribute(coins: coins)
- }
- deinit {
- debugPrint("PlayerOne has left the game")
- Bank.receive(coins: coinsInPurse)
- }
- }
- var playerOne: Player? = Player(coins: 100)
- print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
- // Prints "A new player has joined the game with 100 coins"
- print("There are now \(Bank.coinsInBank) coins left in the bank")
- // Prints "There are now 9900 coins left in the bank
- playerOne!.win(coins: 2_000)
- print("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins")
- // Prints "PlayerOne won 2000 coins & now has 2100 coins"
- print("The bank now only has \(Bank.coinsInBank) coins left")
- // Prints "The bank now only has 7900 coins left
- playerOne = nil
- // Prints "PlayerOne has left the game"
- print("The bank now has \(Bank.coinsInBank) coins")
- // Prints "The bank now has 10000 coins
I learned about this call to Thread.callStackSymbols.forEach
on
stackoverflow,
but the more interesting information is in the reference docs for
Thread
.
You see, callStackSymbols
is an array of String
and therefore it has
a forEach
. The output from running the above program looks like this:
- A new player has joined the game with 100 coins
- There are now 9900 coins left in the bank
- PlayerOne won 2000 coins & now has 2100 coins
- The bank now only has 7900 coins left
- "PlayerOne has left the game"
- 0 ??? 0x00000001153071e5 0x0 + 4650463717
- 1 ??? 0x0000000115307478 0x0 + 4650464376
- 2 libswiftCore.dylib 0x00000001179bda00 _swift_release_dealloc + 16
- 3 ??? 0x00000001153068d5 0x0 + 4650461397
- 4 swift 0x000000010e8a723d _ZN4llvm5MCJIT11runFunctionEPNS_8FunctionENS_8ArrayRefINS_12GenericValueEEE + 365
- 5 swift 0x000000010e8adc1c _ZN4llvm15ExecutionEngine17runFunctionAsMainEPNS_8FunctionERKNSt3__16vectorINS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS8_ISA_EEEEPKPKc + 1004
- 6 swift 0x000000010db0efe4 _ZL14performCompileRN5swift16CompilerInstanceERNS_18CompilerInvocationEN4llvm8ArrayRefIPKcEERiPNS_16FrontendObserverEPNS_20UnifiedStatsReporterE + 52660
- 7 swift 0x000000010dafed35 _ZN5swift15performFrontendEN4llvm8ArrayRefIPKcEES3_PvPNS_16FrontendObserverE + 7717
- 8 swift 0x000000010daa4965 main + 1349
- 9 libdyld.dylib 0x00007fff79108015 start + 1
- The bank now has 10000 coins
Line 14 of the output, 8 swift ... main + 1349
, corresponds exactly to
1349 characters after the start of above code listing, which is
playerOne = nil
. And indeed that is when the deinitializer is called.
Looking at the reference documentation, there are some other interesting
properties of Thread
. Some useful class properties:
- isMultiThreaded: Boolean
- isMainThread: Boolean
- name: the name of the thread
- current: the current thread
Let’s add this line to the beginning of the code listing:
Thread.current.name = "Main Game Thread"
Modifying the above code to print out these properties, the new output is:
isMultiThreaded: false
current.isMainThread: true
current.name: Main Game Thread
So we can see that the deinitializer is called on the same thread as the main code.
I’m sure there is more to reveal about runtime stack inspection, but mastering the diagnostic and debugging techniques of a programming language platform is an important component of proficiency. It pays to pick up and save such tips when they come along.