Why Unsaferawpointer Shows Different Result When Function Signatures Differs in Swift

Why UnsafeRawPointer shows different result when function signatures differs in Swift?

Why the results of two prints differs?

Because for each function call, Swift is creating a temporary variable initialised to the value returned by a.key's getter. Each function is called with a pointer to their given temporary variable. Therefore the pointer values will likely not be the same – as they refer to different variables.

The reason why temporary variables are used here is because A is a non-final class, and can therefore have its getters and setters of key overridden by subclasses (which could well re-implement it as a computed property).

Therefore in an un-optimised build, the compiler cannot just pass the address of key directly to the function, but instead has to rely on calling the getter (although in an optimised build, this behaviour can change completely).

You'll note that if you mark key as final, you should now get consistent pointer values in both functions:

class A {
final var key = "aaa"
}

var a = A()
aaa(&a.key) // 0x0000000100a0abe0
bbb(&a.key) // 0x0000000100a0abe0

Because now the address of key can just be directly passed to the functions, bypassing its getter entirely.

It's worth noting however that, in general, you should not rely on this behaviour. The values of the pointers you get within the functions are a pure implementation detail and are not guaranteed to be stable. The compiler is free to call the functions however it wishes, only promising you that the pointers you get will be valid for the duration of the call, and will have pointees initialised to the expected values (and if mutable, any changes you make to the pointees will be seen by the caller).

The only exception to this rule is the passing of pointers to global and static stored variables. Swift does guarantee that the pointer values you get will be stable and unique for that particular variable. From the Swift team's blog post on Interacting with C Pointers (emphasis mine):

However, interaction with C pointers is inherently
unsafe compared to your other Swift code, so care must be taken. In
particular:

  • These conversions cannot safely be used if the callee
    saves the pointer value for use after it returns. The pointer that
    results from these conversions is only guaranteed to be valid for the
    duration of a call. Even if you pass the same variable, array, or
    string as multiple pointer arguments, you could receive a different
    pointer each time. An exception to this is global or static stored
    variables. You can safely use the address of a global variable as a
    persistent unique pointer value
    , e.g.: as a KVO context parameter.

Therefore if you made key a static stored property of A or just a global stored variable, you are guaranteed to the get same pointer value in both function calls.


Changing the function signature

When I change the function signature of bbb to make it the same with aaa, the result of two prints are the same

This appears to be an optimisation thing, as I can only reproduce it in -O builds and playgrounds. In an un-optimised build, the addition or removal of an extra parameter has no effect.

(Although it's worth noting that you should not test Swift behaviour in playgrounds as they are not real Swift environments, and can exhibit different runtime behaviour to code compiled with swiftc)

The cause of this behaviour is merely a coincidence – the second temporary variable is able to reside at the same address as the first (after the first is deallocated). When you add an extra parameter to aaa, a new variable will be allocated 'between' them to hold the value of the parameter to pass, preventing them from sharing the same address.

The same address isn't observable in un-optimised builds due to the intermediate load of a in order to call the getter for the value of a.key. As an optimisation, the compiler is able to inline the value of a.key to the call-site if it has a property initialiser with a constant expression, removing the need for this intermediate load.

Therefore if you give a.key a non-determininstic value, e.g var key = arc4random(), then you should once again observe different pointer values, as the value of a.key can no longer be inlined.

But regardless of the cause, this is a perfect example of how the pointer values for variables (which are not global or static stored variables) are not to be relied on – as the value you get can completely change depending on factors such as optimisation level and parameter count.


inout & UnsafeMutable(Raw)Pointer

Regarding your comment:

But since withUnsafePointer(to:_:) always has the correct behavior I want (in fact it should, otherwise this function is of no use), and it also has an inout parameter. So I assume there are implementation difference between these functions with inout parameters.

The compiler treats an inout parameter in a slightly different way to an UnsafeRawPointer parameter. This is because you can mutate the value of an inout argument in the function call, but you cannot mutate the pointee of an UnsafeRawPointer.

In order to make any mutations to the value of the inout argument visible to the caller, the compiler generally has two options:

  1. Make a temporary variable initialised to the value returned by the variable's getter. Call the function with a pointer to this variable, and once the function has returned, call the variable's setter with the (possibly mutated) value of the temporary variable.

  2. If it's addressable, simply call the function with a direct pointer to the variable.

As said above, the compiler cannot use the second option for stored properties that aren't known to be final (but this can change with optimisation). However, always relying on the first option can be potentially expensive for large values, as they'll have to be copied. This is especially detrimental for value types with copy-on-write behaviour, as they depend on being unique in order to perform direct mutations to their underlying buffer – a temporary copy violates this.

To solve this problem, Swift implements a special accessor – called materializeForSet. This accessor allows the callee to either provide the caller with a direct pointer to the given variable if it's addressable, or otherwise will return a pointer to a temporary buffer containing a copy of the variable, which will need to be written back to the setter after it has been used.

The former is the behaviour you're seeing with inout – you're getting a direct pointer to a.key back from materializeForSet, therefore the pointer values you get in both function calls are the same.

However, materializeForSet is only used for function parameters that require write-back, which explains why it's not used for UnsafeRawPointer. If you make the function parameters of aaa and bbb take UnsafeMutable(Raw)Pointers (which do require write-back), you should observe the same pointer values again.

func aaa(_ key: UnsafeMutableRawPointer) {
print(key)
}

func bbb(_ key: UnsafeMutableRawPointer) {
print(key)
}

class A {
var key = "aaa"
}

var a = A()

// will use materializeForSet to get a direct pointer to a.key
aaa(&a.key) // 0x0000000100b00580
bbb(&a.key) // 0x0000000100b00580

But again, as said above, this behaviour is not to be relied upon for variables that are not global or static.

Convert Data into UnsafeRawPointer and vice versa in swift

What you need to do here is wrap your function body in a withUnsafeBytes:

func save(canvas: Canvas) {
connect()

let drawingData = canvas.drawing.dataRepresentation()

drawingData.withUnsafeBytes { drawingBuffer in

let drawingPtr = drawingBuffer.baseAddress!

// ... In here you can use drawingPtr, for example:

sqlite3_bind_blob(statement, 1, drawingPtr, Int32(drawingBuffer.count), nil)

// ...
}
}

Inside of the withUnsafeBytes block you must not refer to drawingData itself. Outside of the block, you must not refer to drawingPtr.

The point of withUnsafeBytes is that it ensures there is a contiguous representation of the bytes (making copies if needed), and then provides you a pointer to those bytes that is valid for the duration of the block. This pointer is not valid outside of the block. You must not return it or let it otherwise escape. But within the block, you may use it as a void *. This means you must make sure that sqlite3 does not store drawingPtr past the end of this block, which is why you must put the withUnsafeBytes around the entire prepare/finalize sequence, not just the bind_blob statement.

As a rule, you cannot pass around UnsafeRawPointers to things that you did not allocate yourself. There is no promise that the thing they point to continues to exist when you think it does. In the case of Data, there isn't even a promise that it represents a single block of memory (Data may be backed from a dispatch_data, for example). The way that you access a Data's bytes is using withUnsafeBytes.

Your check function has some mistakes, as well. First, your NSString conversions are unnecessary. This line:

sqlite3_bind_text(statement, 1, NSString(string:dateToStringFormat(dateDate: selectedDate)).utf8String, -1, nil)

Can be written as just:

sqlite3_bind_text(statement, 1, dateToStringFormat(dateDate: selectedDate), -1, nil)

Swift will automatically convert String to C-string when passed to a C function that takes a char *.

This code is simply wrong, and may be why you're getting zero bytes:

let drawingPtr = sqlite3_column_blob(statement, 2)
result.append(Canvas(id: Int(sqlite3_column_int(statement, 0)), date: Date_date, drawing: drawingPtr!.load(as: Data.self)))

A pointer to a Blob is not a Data. You can't just load it this way. You need to know how long it is. This is the code you need there:

// Get the pointer
let drawingPtr = sqlite3_column_blob(statement, 2)!

// Get the length
let drawingLength = Int(sqlite3_column_bytes(statement, 2))

// Copy the bytes into a new Data
let drawing = Data(bytes: drawingPtr, count: drawingLength)

// Now construct your Canvas.
result.append(Canvas(id: Int(sqlite3_column_int(statement, 0)), date: Date_date, drawing: drawing))

Can't update a value in a Dictionary inside an Array?

Swift Dictionary is value type not reference type so you need to set it that dictionary with array after making changes.

var test: [[String: AnyObject]] = [["value": true]]

var aaa: [String: AnyObject] = test[0]
print(aaa["value"]) // prints Optional(1)
aaa["value"] = false
print(aaa["value"]) // prints Optional(0)

//Replace old dictionary with new one
test[0] = aaa

var bbb: [String: AnyObject] = test[0]
print(bbb["value"]) // prints Optional(0)

Or You can try simply this way:

test[0]["value"] = false

Compare pointers to C functions in Swift

Generally, you cannot compare functions for equality in Swift.
However, NSSetUncaughtExceptionHandler returns a

(@convention(c) (NSException) -> Swift.Void)?

i.e. a (optional) C-compatible function, and such a function can be
forcefully converted to an (optional) raw pointer with
unsafeBitCast:

let ptr1 = unsafeBitCast(firstHandler, to: Optional<UnsafeRawPointer>.self) 
let ptr2 = unsafeBitCast(secondHandler, to: Optional<UnsafeRawPointer>.self)

which can then be compared for equality

if ptr1 == ptr2 { .. }

A self-contained example:

func exceptionHandler1(exception : NSException) { }

func exceptionHandler2(exception : NSException) { }

NSSetUncaughtExceptionHandler(exceptionHandler1)
let firstHandler = NSGetUncaughtExceptionHandler()
NSSetUncaughtExceptionHandler(exceptionHandler2)
let secondHandler = NSGetUncaughtExceptionHandler()

let ptr1 = unsafeBitCast(firstHandler, to: Optional<UnsafeRawPointer>.self)
let ptr2 = unsafeBitCast(secondHandler, to: Optional<UnsafeRawPointer>.self)

print(ptr1 == ptr2) // false

Swift, generic function: Why is one argument label needed, the other is not?

Question 1

This is by construction of Swift. From Swift language guide for functions - Function Parameter Names:

By default, the first parameter omits its external name, and the
second and subsequent parameters use their local name as their
external name
. All parameters must have unique local names. Although
it’s possible for multiple parameters to have the same external name,
unique external names help make your code more readable.

...

If you do not want to use an external name for the second or
subsequent parameters of a function, write an underscore (_) instead
of an explicit external name for that parameter.

Note from above that you can supersede this demand by placing an underscore _ in front of 2nd (and onward) parameter name. In your case:

func repeatItem<Item>(item: Item, _ numberOfTimes: Int) -> [Item] { ...

Finally note that this has nothing to do with generics, but with Swift functions in general.


Question 2

Try replacing your line

let strArray: [String] = repeatItem("knock", numberOfTimes:4) //!!!!

with

let strArray = [String](count: 4, repeatedValue: "knock")

This uses the initialiser for array objects with repeated entries.

Error: Trying to put the stack in unreadable memory at:

There are a number of things wrong with what you're doing:

  • Attempting to access self.previousPage within its own getter will call itself recursively.

  • You cannot use &self.previousPage as a stable or unique pointer value, as it'll be a pointer to a temporary variable (because you're dealing a computed property). You cannot therefore use it as the key for an associated object. Swift only guarantees stable and unique pointer values for static and global stored variables (see this Q&A for more info).

  • You should make AdditionalStoredProperties a class-bound protocol (with : class), as you can only add associated objects to Objective-C classes (which, on Apple platforms, Swift classes are built on top of). While you can bridge, for example, a struct to AnyObject (it'll get boxed in an opaque Obj-C compatible wrapper), it is merely that; a bridge. There's no guarantee you'll get the same instance back, therefore no guarantee the associated objects will persist.

  • You probably didn't mean for Title to be an associated type of your protocol; you're not using it for anything (the generic placeholder Title defined by getAssociatedObject(key:defValue:) is completely unrelated).

Bearing those points in mind, here's a fixed version of your code:

protocol AdditionalStoredProperties : class {
func getAssociatedObject<T>(ofType: T.Type, key: UnsafeRawPointer,
defaultValue: @autoclosure () -> T) -> T
}

extension AdditionalStoredProperties {

func getAssociatedObject<T>(ofType: T.Type, key: UnsafeRawPointer,
defaultValue: @autoclosure () -> T) -> T {

// or: return objc_getAssociatedObject(self, key) as? T ?? defaultValue()
guard let actualValue = objc_getAssociatedObject(self, key) as? T else {
return defaultValue()
}
return actualValue
}
}

extension UIViewController : AdditionalStoredProperties {

private enum AssociatedObjectKeys {
static var previousPage: Never?
}

var previousPage: String {
get {
// return the associated object with a default of "" (feel free to change)
return getAssociatedObject(ofType: String.self,
key: &AssociatedObjectKeys.previousPage,
defaultValue: "")
}
set {
objc_setAssociatedObject(self, &AssociatedObjectKeys.previousPage,
newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
}

Note that we're:

  • Using a static stored property in order to get a pointer value to use as the key for our associated object. Again, this works because Swift guarantees stable and unique pointer values for static and global stored variables.

  • Using @autoclosure for the defaultValue: parameter, as it may not need to be evaluated if an associated object is already present.

  • Having the key: parameter take an UnsafeRawPointer, as the type of the pointee is irrelevant; it's merely the location in memory that's used as the key.

  • Explicitly satisfying the generic placeholder with an ofType: parameter. This is mainly a matter of preference, but I prefer to spell these things out explicitly rather than relying on type inference.

  • Using camelCase instead of snake_case, as is Swift convention.

Printing &self in swift, cannot assign to immutable value of type

As already explained in the other answer, you cannot pass a constant
as the argument for an in-out parameter. As a workaround, you can
pass an array, as this will actually pass the address of the first
element:

func printMyPointer() {
printPointer([self])
}

But even simpler, use unsafeAddressOf():

func printMyPointer() {
print(unsafeAddressOf(self))
}

Update for Swift 3: As of Xcode 8 beta 6, unsafeAddressOf
does not exist anymore. You can convert self to a pointer:

    print(Unmanaged.passUnretained(self).toOpaque())

or

    print(unsafeBitCast(self, to: UnsafeRawPointer.self))

Cast a Swift struct to UnsafeMutablePointer Void

As far as I know, the shortest way is:

var myStruct = TheStruct()
var address = withUnsafeMutablePointer(&myStruct) {UnsafeMutablePointer<Void>($0)}

But, why you need this? If you want pass it as a parameter, you can (and should):

func foo(arg:UnsafeMutablePointer<Void>) {
//...
}

var myStruct = TheStruct()
foo(&myStruct)


Related Topics



Leave a reply



Submit