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