---
title: "Detecting retain cycles and improved logging"
slug: detecting-retain-cycles-and-improved-logging
section: tech
date: 2016-04-18T14:43:00.000Z
canonical: https://roland.leth.ro/blog/tech/detecting-retain-cycles-and-improved-logging
---

I think adding everywhere a `deinit` method, with a `print` statement inside, is a decent first barrier against retain cycles:

```swift
deinit {
  print("Object X has been deinitialized.")
}
```

This way, if you expect `object x` to `deinit` at some point but it doesn't, you at least know you need to start searching, and where.

Now, for the improved printing, to make this a bit cleaner and easier:

```swift
func customPrint<T>(
  _ object: T,
  _ function: String = #function, // 1
  _ file: String = #file, // 2
  _ line: UInt = #line) { // 3
  
  #if DEBUG // 4
    let filename = URL(string: file)?
      .lastPathComponent
      .stringByReplacingOccurrencesOfString(".swift", withString: "")
      ?? ""
    
    if object is EmptyPrintFlagger { // 5
      print("-- \(filename).\(function) [\(line)]") // 6
    }
    else {
      print("-- \(filename).\(function) [\(line)] - \(object)") // 7
    }
  #endif
}

func customPrint( // 8
  _ function: String = #function,
  _ file: String = #file,
  _ line: UInt = #line) {
  customPrint(EmptyPrintFlagger(), function, file, line) // 9
}

fileprivate struct EmptyPrintFlagger { } // 10
```

First, a few output examples, then we'll break down everything:

```swift
// ObjectX.swift
class ObjectX {

  deinit {
    customPrint() // 11 => ObjectX.deinit [60]
    customPrint("Example") // => ObjectX.deinit [61] - Example
    
    let i: Int? = nil
    let j: Int? = 5
    
    customPrint(i) // => ObjectX.deinit [66] - nil
    customPrint(j) // => ObjectX.deinit [67] - Optional(5)
  }
  
}
```

First, the default parameters: they are built-in keywords that return the current function (1), file (2) and line number (3). You can pass in any `String`, but the purpose of these is to print the current location of `customPrint`.

Next, we make sure no printing happens in production (2), because it (slightly) slows down the app, and clutters the system console with our logs.

Finally, we will do a bit of extra work now, so we can be lazy at later times: we create a dummy, `fileprivate struct` (10), that acts as a flag inside our _main_ `customPrint` (5). This way we are able to call `customPrint` without any parameters (8, 11), but still get the correct location (5, 6, 9, 10). 

Sadly, I didn't find any better way to differentiate between `customPrint()` and `customPrint(x)`. While it wouldn't have been hard to just call `customPrint("")`, I hated the dangling `-` (7 with an empty `String`), and I'm also that lazy sometimes ¯\\\_(ツ)\_/¯.