I want to see how swift String struct pointer changes when I append new string to original one, comparing to Objective C. For Objective C I use this code:
NSMutableString *st1 = [[NSMutableString alloc] initWithString:@"123"];
[st1 appendString:@"456"];
In Objective C st1 string object changes internaly (adds 456 and becomes 123456), but st1 pointer lefts the same and points to the same object. In Swift, since String is not mutable, var st1 must change its address after addition, because it will hold another string, with my strings summ (123456). Is this all correct?
This is my playground code for swift tests:
import Cocoa
var str = "123"
withUnsafePointer(to: &str) { print($0) }
str += "456"
withUnsafePointer(to: &str) { print($0) }
var str2 = "123"
print(Unmanaged<AnyObject>.passUnretained(str2 as AnyObject).toOpaque())
str2 += "456"
print(Unmanaged<AnyObject>.passUnretained(str2 as AnyObject).toOpaque())
and this is results:
0x000000010e228c40 // this are same
0x000000010e228c40
0x00007fb26ed5a790 // this are not
0x00007fb26ed3f6d0
// and they completly different from first two
Why I get same pointer when I use withUnsafePointer? Why I get different pointers when I use Unmanaged.passUnretained? And why pointers received from this methods are completly different?
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.