Why Can't I Pass an Implicitly Unwrapped Optional as an Unsafemutablepointer

Why can't I pass an implicitly unwrapped optional as an UnsafeMutablePointer?

I'm afraid this is another lingering bug around implicitly unwrapped optionals (IUOs).

It has already been fixed though (almost certainly as a result of the recent-ish work to completely remove IUOs from the type system) – it compiles in the latest dev snapshots, and therefore will make it into 4.2 (final re-branch from master is April 20).

Until 4.2 rolls around, one possible workaround would be to use a forwarding computed variable in order to treat the IUO as a strong Optional type:

class SomeClass {}

var obj: SomeClass!
var _optionalObj: SomeClass? {
get { return obj }
set { obj = newValue }
}

func pointerFunc(_: UnsafeMutablePointer<SomeClass?>) {}
pointerFunc(&_optionalObj)

(that is, assuming you're okay with having the pointer point to a temporary value – i.e you're not relying on the pointer value being stable or unique, such as you would be if this were to be used, for example, as an associated object key)

Are implicitly unwrapped optionals truly optionals?

This is a known bug in the swift compiler. Hamish says in a comment this is fixed in a Swift 4.1 snapshot, so it may be fixed in the next Xcode release (9.3).

You can work around this by getting rid of the implicitly-unwrapped optional (IUO), which should be avoided anyway. Depending on why it's currently an IUO, either:

var str: String?
func someFunc(_ x: inout String?) {}
someFunc(&str)

or

var tmp: String?
func someFunc(_ x: inout String?) {}
someFunc(&tmp)
let str = tmp!

I strongly recommend the first, avoid force-unwrapping unless absolutely necessary.

Implicitly unwrapped optional made immutable

Update:

This has been fixed in Xcode Beta 5 with one small caveat:

var list: [Int]! = [1]
list.append(10)

var number: Int! = 1
number! += 2
number += 2 // compile error

The array works as expected, but it seems that right now the integer still requires an explicit unwrap to allow using +=


Currently, this is just the nature of Optionals (whether implicitly unwrapped or not). The unwrap operator returns an immutable value. This is likely to be fixed or a better solution will be provided in the future.

The only way around it for now is to wrap the array in a class:

class IntArray {
var elements : [Int]
}

Implicitly unwrapped optional var destroyed by compiler before end of scope?


But as a programmer we are used to local variables inside of functions being available until leaving the scope.

This hasn't been the case since ARC was first released for ObjC. ARC has always had the option to release objects after their last use (and very often makes use of this). This is by design, and is not a bug in Swift (or in ObjC, where it's also true).

In Swift, if you want to extend the lifetime of an object beyond its last use,withExtendedLifetime is explicitly for this purpose.

var someObject: SomeClass! = SomeClass()

withExtendedLifetime(someObject) {
someObject.doSth()
print("done")
}

Keep in mind that it is legal for objects to have balanced retain/autorelease calls on them, which may cause them to outlive their scope as well. This is much less common in Swift, but still legal and happens if you pass a Swift object to ObjC (which can happen in many places you may not expect).

You should be very careful relying on when deinit will be called. It can surprise you, and isn't even promised in all cases (for example, deinit is not called during program quit on Mac, which tends to surprise C++ developers).

IMO performAsyncSyncTask is a dangerous pattern, and should be redesigned with clearer ownership. I don't do enough RxSwift work to immediately redesign it, but blocking the whole thread on a DispatchSemaphore seems the wrong way to integrate with any reactive system. Threads are a finite resource, and this forces the system to create more while this one is blocked doing nothing.

Swift unary operator with implicitly unwrapped optional


why do unary and binary operators have different parameter
requirements?

Well, it isn't a unary vs. binary question. There are unary operators that work with Int!. For instance:

var i: Int! = 17
var j = -i

- is a unary operator and it works. The question comes back to the issue of inout. The ++ prefix and postfix operators for Int don't work with Int! because the variable is passed as inout (since ++ modifies the original variable in addition to returning a value). inout requires the type to exactly match.

Note that implicitly unwrapped optionals are still optionals.

var i: Int! = 17
var k = i // k has the type Int!
var m = i! // m has the type Int

So passing an implicitly unwrapped optional as an inout variable that requires the non-optional type doesn't work because inout variables need to exactly match the type expected and Int and Int! are two very different types. The variable must either be explicitly unwrapped, or you need to provide an overloaded function that takes the optional type.

You might ask, why doesn't Swift just unwrap the Int! for you and call ++ with the Int? Well, ++ both modifies the variable and returns a value. If Swift unwrapped the Int! and called ++ with an Int, then the type of the return would be Int. Then you'd have people on StackOverflow asking, "why does var i: Int! = 17; var j = i++ make j an Int instead of an Int! ?". To do it right, ++ needs to return an Int when it is given an Int, and return an Int! when it is given an Int!. So, what is needed in an overloaded function.

It is possible to overload ++ and make ++ prefix and postfix functions for Int!:

prefix func ++(inout x: Int!) -> Int! {
return ++x!
}

postfix func ++(inout x: Int!) -> Int! {
return x!++
}

var i: Int! = 17
var j = ++i
print("i = \(i), j = \(j)") // "i = 18, j = 18"
j = i++
print("i = \(i), j = \(j)") // "i = 19, j = 18"

As to why the Swift designers haven't done this, only they know why.

Incrementing an implicitly unwrapped optional

Implicitly unwrapped optional is a type on its own, and is different from the type it that wraps. Some operators on optionals and implicitly unwrapped optionals are pre-defined for you out of the box by the language, but for the rest you have to define them yourself.

In this particular case an operator postfix func --(inout value: Int!) -> Int! is just not defined. If you want to use postfix -- operator on Int! just the same way you use it on Int then you will have to define one.

E.g. something like:

postfix func --<T: SignedIntegerType>(inout value: T!) -> T! {
guard let _value = value else { return nil }

value = _value - 1
return _value
}

Unwrapped non-optional in Swift

A String without a ? or ! at end is just a String. It's not a kind of optional.

How to guarantee that inout argument won't change Type and won't become nil in a function

inout won't accept Generic type , because the reference wants to store value backs to its original value. here is the link for inout https://docs.swift.org/swift-book/LanguageGuide/Functions.html

Declare a pointer to a property and pass it as inout param into func in Swift?

After discussion in comments, the question is intended to be specifically how to work with UnsafeMutablePointer to achieve the result, and less about the best way to achieve the result.

It's important that the entirety of the code from the point of acquiring the pointer to using it be scoped within withUnsafeMutablePointer. Because it comes down to selecting between two arrays, and then passing one of them via an alias to someFunc, you don't know which pointer has to be kept live, so you have to keep them both live. Otherwise your program will likely crash when Swift invalidates it.

The proper and safe way to achieve the desired effect with pointers is like this:

func pointerSolution()
{
var sourceArray1 = [1, 2, 3, 4]
var sourceArray2 = [5, 6]

func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[$0] = 0 }
}

let someCase = true // just for the sake of compiling a concrete case

//usage code
withUnsafeMutablePointer(to: &sourceArray1)
{ src1 in
withUnsafeMutablePointer(to: &sourceArray2)
{ src2 in
// Substitute appropriate type for `Int`
let arrayPtr: UnsafeMutablePointer<[Int]>
if someCase {
arrayPtr = src1
// Some additional code that may or may not involve arrayPtr
} else {
arrayPtr = src2
// Some additional code that may or may not involve arrayPtr
}
someFunc(&arrayPtr.pointee)
}
}

print("sourceArray1 = \(sourceArray1)")
print("sourceArray2 = \(sourceArray2)")
}

If you have to do this is in multiple places, or just want to clean up the syntactic bloat of nesting withUnsafeMutablePointer blocks, you can provide a helper function:

func withUnsafeMutablePointers<T, R>(
to value1: inout T,
and value2: inout T,
_ body: (UnsafeMutablePointer<T>, UnsafeMutablePointer<T>) throws -> R) rethrows -> R
{
try withUnsafeMutablePointer(to: &value1)
{ ptr1 in
try withUnsafeMutablePointer(to: &value2)
{ ptr2 in
try body(ptr1, ptr2)
}
}
}

Then where you use it, you have one level of nesting:

func pointerSolution()
{
var sourceArray1 = [1, 2, 3, 4]
var sourceArray2 = [5, 6]

func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[$0] = 0 }
}

let someCase = true // just for the sake of compiling a concrete case

//usage code
withUnsafeMutablePointers(to: &sourceArray1, and: &sourceArray2)
{ src1, src2 in
// Substitute appropriate type for `Int`
let arrayPtr: UnsafeMutablePointer<[Int]>
if someCase {
arrayPtr = src1
// Some additional code that may or may not involve arrayPtr
} else {
arrayPtr = src2
// Some additional code that may or may not involve arrayPtr
}
someFunc(&arrayPtr.pointee)
}

print("sourceArray1 = \(sourceArray1)")
print("sourceArray2 = \(sourceArray2)")
}

If you want to live dangerously, you can do this:

func dangerousPointerSolution()
{
var sourceArray1 = [1, 2, 3, 4]
var sourceArray2 = [5, 6]

func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[$0] = 0 }
}

let someCase = true // just for the sake of compiling a concrete case

//usage code
let address: Int
if someCase {
address = withUnsafeMutablePointer(to: &sourceArray1) { Int(bitPattern: $0) }
// Some additional code that may or may not involve address
} else {
address = withUnsafeMutablePointer(to: &sourceArray2) { Int(bitPattern: $0) }
// Some additional code that may or may not involve address
}
someFunc(&(UnsafeMutablePointer<[Int]>(bitPattern: address)!).pointee)

print("sourceArray1 = \(sourceArray1)")
print("sourceArray2 = \(sourceArray2)")
}

Note the pointer conversion through Int. This is because when withUnsafeMutablePointer returns, it invalidates the pointer $0 internally, and if you were to just return $0, the pointer that is returned by withUnsafeMutablePointer is also invalidated. So you have to trick Swift into giving you something you can use outside of withUnsafeMutablePointer. By converting it to an Int, you're basically saving off the valid address as a numeric value. Swift can't invalidate it. Then outside of withUnsafeMutablePointer you have to convert that Int address back into a pointer, which UnsafeMutablePointer<T> has an initializer to do (after all, you can imagine an embedded system with memory mapped I/O. You'd need to read/write to a specific address to do I/O.) Any time you have to trick the compiler into letting you do something, it should be a big red flag that maybe you shouldn't be doing that. You may still have good reasons, but at the very least, it should cause you to question them, and consider alternatives.

It's important that you don't use address to reconstruct another pointer outside of this scope. In this specific example, it remains a valid address within the function scope only because it's an address for local values that are referred to after the pointer is used. When those values go out of scope, using any pointer to them becomes a problem. If they were properties of a class letting the address escape outside of the scope of the class would be a problem when the instance is deinitialized. For a struct the problem happens sooner since it's likely it would end up being used on a copy of the struct rather than on the original instance.

In short, when using pointers, keep them as local as possible, and be sure they, or anything that can be used to reconstruct them without the original Swift "object" they point to, don't escape outside the context in which you know for sure that they are valid. This isn't C. You don't have as much control over the lifetime of allocated memory. Normally in Swift you don't have to worry about it, but when you do use pointers, it's actually harder to reason about their validity than it is in C for the very reason that you don't get to specify when allocated memory becomes invalid. For example, in Swift, it's not guaranteed that a locally allocated instance of a class will remain "live" through the end of the scope. In fact, it's often deinitialized immediately after its last use, even though there may be more code after in the same scope. If you have a pointer to such an object, even when you are still the same scope, you may now be pointing to deinitialized memory. Swift even has to provide withExtendedLifetime to deal with such cases. That's why Swift tries to limit their use solely within the withUnsafePointer family of functions. That's the only context that it can guarantee their validity. There are other contexts in which they would be valid, but the compiler can't prove that they are.



Related Topics



Leave a reply



Submit