问题
There are cases when you have to deal with a structure of certain type, but the upstream API requires you to present it though a pointer to another type at other places.
For instance, Unix Bind is expecting its second argument to be a pointer to a sockaddr
, whereas the constructor should be sockaddr_in
.
For now I'm sticking to a two-layer with*
:
var sa = sockaddr_in(/* ... */)
withUnsafePointer(to: &sa) { _sa in
_sa.withMemoryRebound(to: sockaddr.self, capacity: 1) { __sa in
let err = bind(fd, __sa, socklen_t(saSize))
precondition(err == 0)
}
}
However, I'm discouraged by noisiness of this approach. When I use unsafeBitCast
between pointer types:
bind(fd, unsafeBitCast(__sa, to: UnsafeMutablePointer<sockaddr>.self), socklen_t(saSize))
Then the compiler warns me not to do so, and recommends resort to withMemoryRebound
.
When I use an in-place constructed pointer:
UnsafeMutablePointer(mutating: &sa).withMemoryRebound(to: sockaddr.self, capacity: 1) { _sa in
let err = bind(fd, _sa, socklen_t(saSize))
precondition(err == 0)
}
Then it works as well as the initial version, and rids us of one level of nesting. Although it looks more frail than with*
. Also it's unclear, if the in-place pointer is the correct approach, why withUnsafePointer
even exists.
Having that said, what is the canonical way to reinterpret a structure in Swift?
回答1:
Your first method is the correct one, using withUnsafePointer()
and withMemoryRebound()
, as one can see from the various examples
provided in UnsafeRawPointer Migration, e.g.
In Swift 3, the user should explicitly rebind memory to a different type:
let result = withUnsafePointer(to: &addr) { // Temporarily bind the memory at &addr to a single instance of type sockaddr. $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { connect(sock, $0, socklen_t(MemoryLayout<sockaddr_in>.stride)) } }
The other approach
UnsafeMutablePointer(mutating: &sa).withMemoryRebound(...) { ... }
looks fragile to me. sa
is passed as an inout
parameter to
the constructor of UnsafeMutablePointer()
, but that may be
the address of temporary storage, and there is no guarantee that
it is still valid when the constructor has returned and the closure
is called.
来源:https://stackoverflow.com/questions/45561367/what-is-the-correct-way-to-reinterpret-an-entity-in-swift