Swift - Call a Method Using a Pointer
The debugger is invaluable while writing any kind of program, but sometimes Swift's type safety makes debugging more difficult than Objective-C. My most common stumbling block in Swift is calling a method with just the address of an object1. A discussion on the #swift-lang
IRC channel finally prompted me to come up with something.
LLDB
While paused at a breakpoint in LLDB, given an object's memory address, construct an UnsafePointer
, cast it using unsafeBitCast
, then call a method or access a member variable on the result. For example, given the address 12345678
of an NSString
instance, get the string's length using this LLDB command:
expr unsafeBitCast(UnsafePointer<NSString>(bitPattern: 12345678), NSString.self).length
That's a lot of typing, and includes some redundancy with the duplicated NSString
type given to both UnsafePointer
and unsafeBitCast
. Fortunately, LLDB lets us create a custom command to use as shorthand for all of that. I used command regex
to accept the address, class, and method/variable as arguments, calling my command ptrcall
. To create the command, pause a program in LLDB, then run it in the LLDB prompt:
command regex ptrcall 's/(.+) (.+) (.+)/expr unsafeBitCast(UnsafePointer<%2>(bitPattern: %1), %2.self).%3/'
Later, while paused in the same instance of the debugger, use the command by providing the memory address, class name, and variable/method:
ptrcall 12345678 NSString length
~/.lldbinit
Adding the command during every debugging session is a real drag, but LLDB gives us a way to save it between runs. Just add it to your ~/.lldbinit
and it will be available in every debugging session. If you don't have an .lldbinit
in your home directory, go ahead and create it. Add the command with the same syntax as before:
command regex ptrcall 's/(.+) (.+) (.+)/expr unsafeBitCast(UnsafePointer<%2>(bitPattern: %1), %2.self).%3/'
Or a Swift struct's, or a Swift enum's memory address. This post applies to all of these Swift constructs.↩