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.

  1. import Foundation
  2.  
  3. class Bank {
  4.     static var coinsInBank = 10_000
  5.     static func distribute(coins numberOfCoinsRequested: Int) -> Int {
  6.         let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)
  7.         coinsInBank -= numberOfCoinsToVend
  8.         return numberOfCoinsToVend
  9.     }
  10.     static func receive(coins: Int) {
  11.         coinsInBank += coins
  12.     }
  13. }
  14.  
  15. class Player {
  16.     var coinsInPurse: Int
  17.     init(coins: Int) {
  18.         coinsInPurse = Bank.distribute(coins: coins)
  19.     }
  20.     func win(coins: Int) {
  21.         coinsInPurse += Bank.distribute(coins: coins)
  22.     }
  23.     deinit {
  24.         debugPrint("PlayerOne has left the game")
  25.         Thread.callStackSymbols.forEach{print($0)}
  26.         Bank.receive(coins: coinsInPurse)
  27.     }
  28. }
  29.  
  30.  
  31. var playerOne: Player? = Player(coins: 100)
  32. print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
  33. // Prints "A new player has joined the game with 100 coins"
  34. print("There are now \(Bank.coinsInBank) coins left in the bank")
  35. // Prints "There are now 9900 coins left in the bank
  36.  
  37. playerOne!.win(coins: 2_000)
  38. print("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins")
  39. // Prints "PlayerOne won 2000 coins & now has 2100 coins"
  40. print("The bank now only has \(Bank.coinsInBank) coins left")
  41. // Prints "The bank now only has 7900 coins left
  42.  
  43. playerOne = nil
  44. // Prints "PlayerOne has left the game"
  45. print("The bank now has \(Bank.coinsInBank) coins")
  46. // 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:

  1. A new player has joined the game with 100 coins
  2. There are now 9900 coins left in the bank
  3. PlayerOne won 2000 coins & now has 2100 coins
  4. The bank now only has 7900 coins left
  5. "PlayerOne has left the game"
  6. 0   ???                                 0x00000001153071e5 0x0 + 4650463717
  7. 1   ???                                 0x0000000115307478 0x0 + 4650464376
  8. 2   libswiftCore.dylib                  0x00000001179bda00 _swift_release_dealloc + 16
  9. 3   ???                                 0x00000001153068d5 0x0 + 4650461397
  10. 4   swift                               0x000000010e8a723d _ZN4llvm5MCJIT11runFunctionEPNS_8FunctionENS_8ArrayRefINS_12GenericValueEEE + 365
  11. 5   swift                               0x000000010e8adc1c _ZN4llvm15ExecutionEngine17runFunctionAsMainEPNS_8FunctionERKNSt3__16vectorINS3_12basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEENS8_ISA_EEEEPKPKc + 1004
  12. 6   swift                               0x000000010db0efe4 _ZL14performCompileRN5swift16CompilerInstanceERNS_18CompilerInvocationEN4llvm8ArrayRefIPKcEERiPNS_16FrontendObserverEPNS_20UnifiedStatsReporterE + 52660
  13. 7   swift                               0x000000010dafed35 _ZN5swift15performFrontendEN4llvm8ArrayRefIPKcEES3_PvPNS_16FrontendObserverE + 7717
  14. 8   swift                               0x000000010daa4965 main + 1349
  15. 9   libdyld.dylib                       0x00007fff79108015 start + 1
  16. The bank now has 10000 coins
  17.  

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.