Skip to content

Commit 19b9eee

Browse files
committed
Proposal to change Unmanaged API to use UnsafePointer
1 parent bf04af0 commit 19b9eee

File tree

1 file changed

+83
-0
lines changed

1 file changed

+83
-0
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Change `Unmanaged` to use `UnsafePointer`
2+
3+
* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-convert-unmanaged-to-use-unsafepointer.md)
4+
* Author(s): [Jacob Bandes-Storch](https://github.com/jtbandes)
5+
* Status: **Review**
6+
* Review manager: TBD
7+
8+
## Introduction
9+
10+
The standard library [`Unmanaged<Instance>` struct](https://github.com/apple/swift/blob/master/stdlib/public/core/Unmanaged.swift) provides a type-safe object wrapper that does not participate in ARC; it allows the user to make manual retain/release calls.
11+
12+
## Motivation
13+
14+
The following methods are provided for converting to/from Unmanaged:
15+
16+
```swift
17+
static func fromOpaque(value: COpaquePointer) -> Unmanaged<Instance>
18+
func toOpaque() -> COpaquePointer
19+
```
20+
21+
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
22+
23+
```swift
24+
someFunction(context: UnsafeMutablePointer(Unmanaged.passUnretained(self).toOpaque()))
25+
26+
info.retain = { Unmanaged<AnyObject>.fromOpaque(COpaquePointer($0)).retain() }
27+
info.copyDescription = {
28+
Unmanaged.passRetained(CFCopyDescription(Unmanaged.fromOpaque(COpaquePointer($0)).takeUnretainedValue()))
29+
}
30+
```
31+
32+
## Proposed solution
33+
34+
In the `Unmanaged` API, replace the usage of `COpaquePointer` with `UnsafePointer<Void>` and `UnsafeMutablePointer<Void>`.
35+
36+
The affected functions are `fromOpaque()` and `toOpaque()`. Only very minor modification is required from the [current implementation](https://github.com/apple/swift/blob/0287ac7fd94af0fb860b5444e1bd26faded88e39/stdlib/public/core/Unmanaged.swift#L32-L54):
37+
38+
```swift
39+
@_transparent
40+
@warn_unused_result
41+
public static func fromOpaque(value: UnsafePointer<Void>) -> Unmanaged {
42+
// Null pointer check is a debug check, because it guards only against one
43+
// specific bad pointer value.
44+
_debugPrecondition(
45+
value != nil,
46+
"attempt to create an Unmanaged instance from a null pointer")
47+
48+
return Unmanaged(_private: unsafeBitCast(value, Instance.self))
49+
}
50+
51+
@_transparent
52+
@warn_unused_result
53+
public func toOpaque() -> UnsafeMutablePointer<Void> {
54+
return unsafeBitCast(_value, UnsafeMutablePointer<Void>.self)
55+
}
56+
```
57+
58+
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()`.
59+
60+
The example usage above no longer requires conversions:
61+
62+
```swift
63+
someFunction(context: Unmanaged.passUnretained(self).toOpaque())
64+
65+
info.retain = { Unmanaged<AnyObject>.fromOpaque($0).retain() }
66+
info.copyDescription = {
67+
Unmanaged.passRetained(CFCopyDescription(Unmanaged.fromOpaque($0).takeUnretainedValue()))
68+
}
69+
```
70+
71+
## Impact on existing code
72+
73+
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:
74+
75+
@available(*, unavailable, message="use fromOpaque(value: UnsafeMutablePointer<Void>) instead")
76+
@available(*, unavailable, message="use toOpaque() -> UnsafePointer<Void> instead")
77+
78+
[Code that uses `COpaquePointer`](https://github.com/search?q=COpaquePointer&type=Code) does not seem to depend on it heavily, and would not be significantly harmed by this change.
79+
80+
## Alternatives considered
81+
82+
- Make no change. However, it has been [said on swift-evolution](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001096.html) that `COpaquePointer` is vestigial, and better bridging of C APIs is desired, so we do want to move in this direction.
83+

0 commit comments

Comments
 (0)