[swift-evolution] RFC: Proposed rewrite of Unmanaged<T>

Jordan Rose jordan_rose at apple.com
Sat Dec 19 22:13:56 CST 2015


> On Dec 19, 2015, at 19:43 , Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
> 
>> 
>> On Dec 19, 2015, at 4:22 PM, Brent Royal-Gordon <brent at architechies.com <mailto:brent at architechies.com>> wrote:
>> 
>>>> Mainly, because simply saying "release" or "released" is a bit ambiguous to me.Are you saying it *has been* released, or are you saying it *needs to be* released?
>>> 
>>> But nobody proposed "released" as a method name.  In what way is "release" ambiguous?  It's an imperative verb.
>> 
>> I guess you're right that "release" is unambiguous, but as you mentioned, it's also strange to release a value and then use it.
> 
> Yes.  I think there are no really great choices here (at least not so far) so the question is whether that strangeness is enough of a problem to outweigh the points release() has in its favor.  What do you think?
> 
>> I think what I'm trying to get at here is that I prefer to think of the operations on Unmanaged as "explain to ARC how it should handle this object", rather than "do some manual operations so that ARC will do the right thing". Maybe the current Unmanaged design has shown the limitations of that approach, though.
> 
> Not at all; the Unmanaged design—at least in my best understanding of its intent—is firmly in the imperative/manual operations camp.  I wanted to do something more declarative, but the "I want to manage the reference that I claim was passed to me at +1" operation is side-effectful. Are we really comfortable with hiding that fact?
> 
>>> But you applied "take" to both of them?  One of them is idempotent while the other is not.
>> 
>> The preferred way to use Unmanaged is that you immediately convert it to a managed reference without ever storing it or using it in any other way. That means you should immediately call either the retain-and-return operation or the don't-retain-and-return operation. Both of these should only ever be called once. You may instead choose to keep the reference Unmanaged and manually retain, release, and access it, but best practices discourage that.
> 
> As I said in my original post, I'm ambivalent about the importance of highlighting the distinctions of safety and idempotence between these methods, but even if they're named similarly I don't see any merit in starting with "take."  One thing I really dislike about is that the receiver, the UnsafeReference, isn't "taking" anything.  The *caller* might be said to be taking something from the UnsafeReference, but only in the "returned at +1" case.  
> 
> How I see it: along with the UnsafeReference the called CF function either notionally
> a) gives (possibly-shared) ownership of the object directly to the caller, or
> b) gives the caller a token that allows him to get (shared) ownership of the object
> 
> In case a), the caller needs to ask the UnsafeReference to transfer (or "release") that ownership into a strong reference, and.  In case b), the caller needs to explicitly get (shared) ownership.
> 
> If this description doesn't sound right to you, please try to correct it; that may help me understand your perspective better.

For the record, I have previously talked with Dave about this in person. The conclusion I came to was that "[foo release]" and "CFRelease(foo)" are saying "I release [my hold on] 'foo'", but std::unique_ptr::release <http://en.cppreference.com/w/cpp/memory/unique_ptr/release> is saying "please release your referent to me". The directionality being different is what created so much cognitive dissonance for me.

I'm still one of those who really doesn't like plain 'release' as a name. Putting "release" in the name is fine (to Greg's point about CFBridgingRelease), but 'release' on its own has way too much baggage for me. (To the point where I have to double-check mentally that it's what I really want when I'm working with std::unique_ptr.) Maybe that's historical, though—a pure Swift programmer has never called CFRelease.

To Brent's point about putting "Create" somewhere in there: the other use of Unmanaged is "safely" getting managed references through 'void *' "context pointers". In this case you're balancing a retain you performed. (That said, I'm also against the general pattern of passing references through context pointers, because it's very hard to ensure that they get cleaned up, if retained, or kept alive, if unretained.)

Oh, and there's one last use of Unmanaged: fields of structs. These are rare but they do exist.

Jordan

P.S. There is, in fact, a CFBridgingRetain, for going from ObjC ARC to CF manual ref-counting. I'm not sure why Greg didn't mention it.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151219/be17a707/attachment.html>


More information about the swift-evolution mailing list