Withunsafepointer in Swift 2

withUnsafePointer in Swift 2

The error message is misleading, the problem is that
SCNetworkReachabilityCreateWithAddress() does not return an
unmanaged object anymore, so you must not call
takeRetainedValue():

var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)

let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
}

Note also the simplified creation of C structs like struct sockaddr_in which was
introduced with Swift 1.2 (if I remember correctly).

Swift - Difference between passing variable and passing variable address withUnsafePointer?

The differences you see come from the fact that you are using two different overloads of withUnsafePointer, I'll list them in the same order you used them:

func withUnsafePointer<T, Result>(to value: T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result

, and

func withUnsafePointer<T, Result>(to value: inout T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result

The difference between the two is the inout qualifier used for the value parameter.

Now, let's try to understand what happens behind the scenes.

First, 0x00007ffee3362750 looks like a stack pointer, while 0x000000010d226330 looks like a heap pointer. Stack addresses start at the top of the allocated memory for the program, and decrease with every function call (and increase when the function returns).

This indicates that the first overload of withUnsafePointer create a temporary writable variable from the one passed as the argument. This is needed as UnsafePointer needs an inout reference to work with, and a regular parameter is readonly.

This means that the implementation of the non-inout overload of withUnsafePointer looks something like this:

func withUnsafePointer<T, Result>(to value: T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result {
var value = value // shadow the argument, create a readwrite location
return try withUnsafePointer(&value, body)
}

Thus, the first call needs to allocate an intermediate memory location, and this is why you see two different addresses, it's because the addresses are not pointing to the same memory location. However, since both memory locations start with the same value, printing the pointee results in identical output.


Now, let's talk about the array example. The behaviour you see has the same cause: stack allocations. What happens is:

  1. program starts executing, stack pointer has value P (random name)
  2. non-inout withUnsafePointer is called, this is a function call, and stack is reserved for the function; stack pointer is P - N
  3. withUnsafePointer creates the temporary writable variable, and executes
  4. withUnsafePointer returns, and frees the stack memory, at this stack pointer gets back to P
  5. second non-inout withUnsafePointer is called, the stack pointer is back to P - N, however since there were no other function calls between the two, the same stack address is reserved for the temporary writable variable, thus the UnsafePointer instance has the same address

Here, even if the UnsafePointer points to the same address, the values at that address are different, corresponding to values of arr[0] and arr[1].

As for the inout calls, the UnsafePointer point to the actual addresses of the items in the array buffer.

This is how you can get different values for the non-inout calls too:

withUnsafePointer(to: strarr[0]) {
print("\($0)")
print("\($0.pointee)")
}

// this adds a nested function call, which also decreases the stack pointer
// resulting in the temporary location not being on the same stack address
func test() {
withUnsafePointer(to: strarr[1]) {
print("\($0)")
print("\($0.pointee)")
}
}
test()

Directly assign UnsafePointer

I don't know the precise rationale, but presumably let ptr = &index isn't allowed because there's no guarantee that you can dereference ptr without invoking undefined behaviour (assuming index isn't a global or static stored variable – the only cases where Swift guarantees stable and unique pointer values).

Unlike other languages, Swift doesn't guarantee that a local variable will remain initialised until the end of the scope it's declared in – the optimiser is free to deinitialise it earlier. Allowing let ptr = &index would therefore make it far too easy to write unsound code.

It's worth noting that your example of:

func point(num: UnsafePointer<Int32>) -> UnsafePointer<Int32> {
return num
}
let ptr = point(num: &index)

is also unsound. Attempting to dereference ptr from let ptr = point(num: &index) is undefined behaviour, as the inout-to-pointer argument conversion produces a temporary pointer only valid for the duration of the function call.

If you want a scoped temporary pointer to a value, you can use withUnsafePointer(to:) – for example:

func baz() {
var foo = 5
withUnsafePointer(to: &foo) { ptr in
// use `ptr` here – do not escape it!
}

// In Swift 4.2 you can also use `withUnsafePointer(to:)` on let constants.
let bar = 5
withUnsafePointer(to: bar) { ptr in
// use `ptr` here – do not escape it!
}
}

Note that the pointer is only valid for the duration of the closure – attempting to escape it will lead to undefined behaviour.

What difference in withUnsafePointer and Unmanaged.passUnretained

To better explain the behaviour you're seeing, we can actually look at the String source code.

Here's the full definition of String

public struct String {
/// Creates an empty string.
public init() {
_core = _StringCore()
}

public // @testable
init(_ _core: _StringCore) {
self._core = _core
}

public // @testable
var _core: _StringCore
}

So String is just a wrapper around some type called _StringCore. We can find its definition here. Here are the relevant parts:

public struct _StringCore {
//...

public var _baseAddress: UnsafeMutableRawPointer?
var _countAndFlags: UInt

//...
}

As you can see, the _StringCore doesn't directly contain the buffer of memory that stores the string's content. Instead, it references it externally, via a UnsafeMutableRawPointer.

The first time you declare str, it was given some memory on the stack, at address 0x000000010e228c40. When you made a change to str, you actually had no effect on this String struct's location. Instead, you caused the _baseAddress of the String's _core to change. Array works a very similar way. This is how the string's copy-on-write behaviour is implemented, too.

As for the Unmanaged behaviour, str2 as AnyObject creates a copy of str2, so you end up making 2 different copies, hence the difference in
the printed addressed.

How to pass argument in Swift to C function that takes an UnsafePointer?

withUnsafePointer(to:) must be used here:

let h3Index = withUnsafePointer(to: geoCoord) {
(pointer: UnsafePointer<GeoCoord>) -> H3Index in
return geoToH3($0, 5)
}

or shorter (using shorthand parameter syntax and implicit return):

let h3Index = withUnsafePointer(to: geoCoord) { geoToH3($0, 5) }
  • withUnsafePointer(to:) calls the closure with a pointer to the
    geoCoord value.
  • The C function is called with that pointer as the first argument.
  • The return value from the C function is the return value of withUnsafePointer(to:) and assigned to h3Index.

It is important that the pointer is only valid during the execution of withUnsafePointer(to:) and must not be stored or returned for later use.

As an example, the following would be undefined behaviour:

let pointer = withUnsafePointer(to: geoCoord) { return $0 }
let h3Index = geoToH3(pointer, 5)

How to find out the memory area of an optional in swift?

You need to use withUnsafePointer(to:).

var optionalInt: Int?
print("optionalInt = \(optionalInt)")
withUnsafePointer(to: optionalInt, { pointer in
print("optionalInt: \(pointer)")
})
optionalInt = 1
print("optionalInt = \(optionalInt)")
var optionalInt2 = optionalInt
print("optionalInt2 = \(optionalInt)")
withUnsafePointer(to: optionalInt2, { pointer in
print("optionalInt2: \(pointer)")
})
optionalInt2 = 2
print("optionalInt2 = \(optionalInt)")

Output:

optionalInt = nil

optionalInt: 0x00007ffee58ede90

optionalInt = Optional(1)

optionalInt2 = Optional(1)

optionalInt2: 0x00007ffee58ede28

optionalInt2 = Optional(1)



Related Topics



Leave a reply



Submit