-
-
Save amomchilov/a664fbd2bb0da47a219f826f2a35703e to your computer and use it in GitHub Desktop.
https://stackoverflow.com/questions/61236195/create-a-weak-unsafemutablerawpointer/61236529#61236529
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Dispatch | |
import Foundation | |
// The Weak struct is the weak wrapper | |
struct Weak<T: AnyObject> { | |
weak var object: T? | |
} | |
// Stand-in for AUGraphAddRenderNotify | |
func call( | |
cCallback: @escaping @convention(c) (UnsafeMutableRawPointer) -> Void, | |
inRefCon: UnsafeMutableRawPointer | |
) { | |
cCallback(inRefCon) // call once immediately | |
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { | |
cCallback(inRefCon) // call once after deallocation of the TestObject | |
} | |
} | |
// A callback that's guarenteed to not capture a TestObject | |
let theCallBack: @convention(c) (UnsafeMutableRawPointer) -> Void = { rawPointer in | |
print("Running callback") | |
let weakWrapperPointer = rawPointer.assumingMemoryBound(to: Weak<TestObject>.self) | |
let weakWrapper = weakWrapperPointer.pointee | |
if let object = weakWrapper.object { | |
print(object.value) | |
} | |
} | |
// TestObject represents the AudioPlayer class | |
class TestObject { | |
let value = "test" | |
init () { | |
print("init") | |
// Create a wrapper for this object | |
let weakWrapper = Weak<TestObject>(object: self) | |
// Create the wrapper pointer | |
let size = MemoryLayout<Weak<TestObject>>.size | |
let weakWrapperPointer = UnsafeMutableRawPointer.allocate(byteCount: size, alignment: 1) | |
weakWrapperPointer.storeBytes(of: weakWrapper, as: Weak<TestObject>.self) | |
// This is the problem. I can't figure out how to get this to work without crashing | |
call(cCallback: theCallBack, inRefCon: weakWrapperPointer) | |
} | |
deinit { | |
print("deinit") | |
} | |
} | |
// Create a test object, which immediately gets deallocated (represents the AudioPlayer) | |
_ = TestObject() | |
RunLoop.main.run(until: Date().addingTimeInterval(2)) | |
print("The end") |
I think this may be a simpler solution:
class WeakVar<T: AnyObject> {
weak var object: T?
init(object: T) {
self.object = object
}
}
// Stand-in for AUGraphAddRenderNotify
func call(
cCallback: @escaping @convention(c) (UnsafeMutableRawPointer) -> Void,
inRefCon: UnsafeMutableRawPointer
) {
cCallback(inRefCon) // call once immediately
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
cCallback(inRefCon) // call once after deallocation of the TestObject
}
}
// A callback that's guarenteed to not capture a TestObject
let theCallBack: @convention(c) (UnsafeMutableRawPointer) -> Void = { rawPointer in
print("Running callback")
let weakWrapper = Unmanaged<WeakVar>.fromOpaque(opaque).takeRetainedValue()
if let object = weakWrapper.object {
print(object.value)
}
}
class TestObject {
init() {
let weakVar = Weak<TestObject>(object: self)
let opaque = Unmanaged.passRetained(weakVar).toOpaque()
call(cCallback: theCallBack, inRefCon: opaque)
}
}
@mori-nobuteru if I understand correctly, you’re passing in a strong reference to a WeakVar object (now a class), whereas I was passing in a a raw pointer to the struct.
I like your approach better! It involves one more reference counted object, but it’s semantics are clearer
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Copying my answer here for passers-by:
storeBytes(of:as:)
doesn't work here. Look at the documentationstruct Weak<T>
isn't a trivial type, because it has an implicit deinitializer which decrements the reference count of the weak ref's side table entry.What's happening is that the deinit runs are the end of
TestObject.init
, when theweakWrapper
local variable goes out of scope.But then, hen you load it from the raw pointer, you're copying the same weak ref, and tryign to deinitialize again, causing a double-free error
Instead, I move initialize the pointer's destination, destroying the
weakWrapper
local variable (without running its deinitializer, thus not decrementing the weak ref)