I need to choose one of some properties and pass it by reference to set it inside the func. Approximate code:
var someProperty = [SomeClass]()
var someProperty2 = [SomeClass]()
func someFunc(someObject: inout [SomeClass]) {
...
someObject = ... //
}
//usage code
let obj: [SomeClass]
if someCase {
obj = self.someProperty
...
} else {
obj = self.someProperty2
...
}
someFunc(&obj)
The problem is obj is cannot be used as inout parameter but even if I declare it as var then obj is changed but not someProperty or someProperty2.
I read that somehow I need to declare obj as UnsafeMutablePointer and there are similar questions. But I don't know how to apply them to the code above to fix only these 3 lines (without of changing the rest of code):
let obj: [SomeClass]
obj = self.someProperty
obj = self.someProperty2
How to solve this issue?
P.S. in other words I need something like let obj: inout [SomeClass] but it is not allowed by Swift
After discussion in comments, the question is intended to be specifically how to work with
UnsafeMutablePointerto 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 tosomeFunc, 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:
If you have to do this is in multiple places, or just want to clean up the syntactic bloat of nesting
withUnsafeMutablePointerblocks, you can provide a helper function:Then where you use it, you have one level of nesting:
If you want to live dangerously, you can do this:
Note the pointer conversion through
Int. This is because whenwithUnsafeMutablePointerreturns, it invalidates the pointer$0internally, and if you were to just return$0, the pointer that is returned bywithUnsafeMutablePointeris also invalidated. So you have to trickSwiftinto giving you something you can use outside ofwithUnsafeMutablePointer. By converting it to anInt, you're basically saving off the valid address as a numeric value. Swift can't invalidate it. Then outside ofwithUnsafeMutablePointeryou have to convert thatIntaddress back into a pointer, whichUnsafeMutablePointer<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
addressto 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 aclassletting the address escape outside of the scope of theclasswould be a problem when the instance is deinitialized. For astructthe problem happens sooner since it's likely it would end up being used on a copy of thestructrather 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
withExtendedLifetimeto deal with such cases. That's why Swift tries to limit their use solely within thewithUnsafePointerfamily 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.