Semantics of values obtained from Unmanaged

I'm using Unmanaged to keep a Swift object alive while it's being accessed by a C library via FFI, but the documentation of Unmanaged is a little bit vague on the exact semantics of some of its methods. Several of the functions the C library can call need to access the value inside the Unmanaged temporarily, and since the reference count is guaranteed to be >1 while the C function is running I use takeUnretainedValue to access the value. This seems to work fine despite the fact that naively I would expect the returned reference to perform a release when it goes out of scope. Is this the expected behavior?

Here's some pseudocode to illustrate what I'm talking about

func doSomething(unmanaged: Unmanaged<Obj>) {
    let value = unmanaged.takeUnretainedValue() // We obtain a new reference without affecting the reference count
    // use `value`...
    
    // when the function ends, it seems like `value` ought to be released as it's a normal object reference, which would free the object since it's only at +1. This doesn't seem to happen though.
}
1 Like

That's correct usage. The naming of Unmanaged's operations is pretty narrowly focused on "you're immediately passing it in or out of a Core-Foundation-like function", but we never managed to agree on what should replace / augment it. (I say "we" because the last discussion I remember about this was in the Swift 3 days; maybe there have been more since.)

The intended way to think about this is that takeUnretainedValue() extracts a value that has not yet been retained for you, the caller. The type says it's returning it as an owned reference, therefore there must be a retain that happens at this point, which is the one eventually balanced when the owned value goes out of scope (as you say). (And takeRetainedValue() says "the value has already been retained, let me balance that by not doing a retain now".) The fact that you get a managed value out either way is your hint that "retained" and "unretained" refer to the state before the operation rather than the state of the return value, but it's a pretty cloudy hint.

Using takeUnretainedValue for an Unmanaged "context pointer" with a C library during repeated callbacks and then takeRetainedValue when the context pointer is "freed" is a standard pattern, and you are using it correctly.

5 Likes

Thanks for clarifying! I think some code examples would really help the docs, as well as some clarification as to what "consuming" or "not consuming" an unbalanced retain means. Having had it explained it makes sense to me, but I don't think it's particularly obvious just from the docs what exactly that means.

2 Likes