- Proposal: SE-0017
- Author: Jacob Bandes-Storch
- Status: Accepted for Swift 3
- Review manager: Chris Lattner
The standard library Unmanaged<Instance>
struct provides a type-safe object wrapper that does not participate in ARC; it allows the user to make manual retain/release calls.
Swift Evolution Discussion, Proposed Rewrite Discussion
The following methods are provided for converting to/from Unmanaged:
static func fromOpaque(value: COpaquePointer) -> Unmanaged<Instance>
func toOpaque() -> COpaquePointer
However, C APIs that accept void *
or const void *
are exposed to Swift as UnsafePointer<Void>
or UnsafeMutablePointer<Void>
, rather than COpaquePointer
. In practice, users must convert UnsafePointer
→ COpaquePointer
→ Unmanaged
, which leads to bloated code such as
someFunction(context: UnsafeMutablePointer(Unmanaged.passUnretained(self).toOpaque()))
info.retain = { Unmanaged<AnyObject>.fromOpaque(COpaquePointer($0)).retain() }
info.copyDescription = {
Unmanaged.passRetained(CFCopyDescription(Unmanaged.fromOpaque(COpaquePointer($0)).takeUnretainedValue()))
}
In the Unmanaged
API, replace the usage of COpaquePointer
with UnsafePointer<Void>
and UnsafeMutablePointer<Void>
.
The affected functions are fromOpaque()
and toOpaque()
. Only very minor modification is required from the current implementation:
@_transparent
@warn_unused_result
public static func fromOpaque(value: UnsafePointer<Void>) -> Unmanaged {
// Null pointer check is a debug check, because it guards only against one
// specific bad pointer value.
_debugPrecondition(
value != nil,
"attempt to create an Unmanaged instance from a null pointer")
return Unmanaged(_private: unsafeBitCast(value, Instance.self))
}
@_transparent
@warn_unused_result
public func toOpaque() -> UnsafeMutablePointer<Void> {
return unsafeBitCast(_value, UnsafeMutablePointer<Void>.self)
}
Note that values of type UnsafeMutablePointer
can be passed to functions accepting either UnsafePointer
or UnsafeMutablePointer
, so for simplicity and ease of use, we choose UnsafePointer
as the input type to fromOpaque()
, and UnsafeMutablePointer
as the return type of toOpaque()
.
The example usage above no longer requires conversions:
someFunction(context: Unmanaged.passUnretained(self).toOpaque())
info.retain = { Unmanaged<AnyObject>.fromOpaque($0).retain() }
info.copyDescription = {
Unmanaged.passRetained(CFCopyDescription(Unmanaged.fromOpaque($0).takeUnretainedValue()))
}
Code previously calling Unmanaged
API with COpaquePointer
will need to change to use UnsafePointer
. The COpaquePointer
variants can be kept with availability attributes to aid the transition, such as:
@available(*, unavailable, message="use fromOpaque(value: UnsafeMutablePointer<Void>) instead")
@available(*, unavailable, message="use toOpaque() -> UnsafePointer<Void> instead")
Code that uses COpaquePointer
does not seem to depend on it heavily, and would not be significantly harmed by this change.
- Make no change. However, it has been said on swift-evolution that
COpaquePointer
is vestigial, and better bridging of C APIs is desired, so we do want to move in this direction.