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

Brent Royal-Gordon brent at architechies.com
Sat Dec 19 18:22:31 CST 2015


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

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.

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

Now, one of the preferred, do-only-once operations *happens* to be safe to apply more than once, but I view that as an implementation detail. Both of them *happen* to be implemented in the same way as manual operations (`manuallyRelease()` and `object`), but I view that as an implementation detail, too.

Honestly, I might be happier splitting an UnsafeReference type out of Unmanaged and putting the manual retain/release stuff into that:

	// Unmanaged is a high-level type for moving object references in and out of ARC's control.
	struct Unmanaged<T: class> {
		func created() -> T
		func gotten() -> T
		
		// Also would have stuff for passing, which I haven't even thought about yet
	}
	
	// UnsafeReference is a low-level type for manually managing the retain count of an object.
	struct UnsafeReference<T: class> {
		init(_ object: T)
		init(_ unmanaged: Unmanaged<T>)
		
		var object: T
		
		// Some or all of these might return T
		func retain()
		func release()
		func autorelease()
	}

This puts the discouraged manual operations off in their own type where they'll be available to those who know about them, but not sitting right there on every unaudited call.

>> (I kind of want to suggest that retrieving an object through these calls should destroy the reference so it can't be used again, but I don't think that fits with Swift's mutation model without turning `Unmanaged`/`UnsafeReference` into a reference type and adding lots of overhead.)
> 
> Yes, there's no way to reconcile that with the safety offered by the recommended usage patterns, since you can't mutate an rvalue.

I thought so. That's too bad. (I wonder if the compiler can emit warnings instead, though.)

>> (One possibility would be to have a single call with an enum parameter, like `bridge(.Create)` and `bridge(.Get)`. This would let you use the regular form of the verb.)
> 
> There's no "bridging" going on here, though.  This is simply "turn this unsafe thing into a safe thing in one of two ways"

The "bridge" here comes from the Objective-C bridging casts, but I think there it's meant to refer to toll-free bridging, which is not what's happening in Swift.

If the type name remains `Unmanaged`, then perhaps `manage(_:)` would be better? (I don't like `managing` here because that again implies it's side-effect-free and safe to call more than once.)

> So far, my personal assessment of this direction is that it's no better than what I proposed, and has several weaknesses I'd like to avoid.  In fact, it seems very similar to and roughly as understandable as the current Unmanaged design.  I recognize that this is a highly subjective judgement, so if others disagree with me, I'd really like to hear about it.  This is a tough design space and ultimately, what resonates best with the community is likely to be the best choice.

I understand. I'm obviously struggling with this too, as you can see from how much I'm changing my design based on your replies, rather than defending the design as suggested before.

Ultimately, Unmanaged is an API for handling an abstraction failure. That's inherently going to be tricky and subjective.

-- 
Brent Royal-Gordon
Architechies



More information about the swift-evolution mailing list