I think adding everywhere a deinit
method, with a print
statement inside, is a decent first barrier against retain cycles:
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:
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:
// 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 ¯\_(ツ)_/¯.