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?
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
.