swiftaudiounitunsafemutablepointer

Difference between unsafeBitCast and assumingMemoryBound


When I using Apple Audio Unit v2 API in Swift, I face a bug related to using UnsafeMutableRawPointer, which is used in AURenderCallback. So I made a toy example to examine the situation as shown below:

import Foundation

class MyInfo {
  var i: Int = 3
}

class MyDelegate {
  var info: MyInfo?
}

func renderCallback(_ ref: UnsafeMutableRawPointer) {
  var d: MyDelegate = ref.assumingMemoryBound(to: MyDelegate.self).pointee
  // var d: MyDelegate = unsafeBitCast(ref, to: MyDelegate.self) // crash!
  guard var a = d.info else { return }
  a.i = 32
  print(d.info?.i)
}

var d = MyDelegate()
d.info = MyInfo()
renderCallback(&d) // in real application, this is called by audio toolbox framework
print(d.info?.i)

The code works as intended. But, if I use unsafeBitCast instead of assumingMemoryBound, as the comment in the code shows, the program crashes at runtime. Apparently, two code seems equivalent. What is the core difference?


Solution

  • ref is a pointer pointing to some value of MyDelegate. It is not a MyDelegate itself.

    unsafeBitCast re-interprets the bit pattern of the ref pointer itself as a MyDelegate. assumingMemoryRebound re-interprets the pointee of ref as a MyDelegate instead.

    To make this simpler, let's suppose ref has the bit pattern 0x000000016b13b3d8. Suppose there is an Int value of 1 at the location 0x000000016b13b3d8. unsafeBitCast(ref, to: Int.self) would give you 0x000000016b13b3d8 as an Int, so 6091420632 in base 10. On the other hand, ref.assumingMemoryRebound(to: Int.self).pointee would give you 1.

    To use C as an analogy, ref is a void *. ref.assumingMemoryRebound(to: MyDelegate.self) and then getting its point is analogous to *((MyDelegate *)ref) - casting to a MyDelegate pointer and then dereferencing it.

    unsafeBitCast(ref, to: MyDelegate.self) would be analogous to (MyDelegate)ref, casting the pointer directly to a MyDelegate.