Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
88 lines (60 sloc) 4.25 KB

Change Unmanaged to use UnsafePointer

Introduction

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, Review

Motivation

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 UnsafePointerCOpaquePointerUnmanaged, 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()))
}

Proposed solution

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()))
}

Impact on existing code

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.

Alternatives considered

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