withUnsafePointer 和 Unmanaged.passUnretained 有什么区别

What difference in withUnsafePointer and Unmanaged.passUnretained

我想看看 swift 与 Objective C 相比,当我将新字符串附加到原始字符串时,字符串结构指针如何变化。对于 Objective C 我使用此代码:

NSMutableString *st1 = [[NSMutableString alloc] initWithString:@"123"];
[st1 appendString:@"456"];

In Objective C st1 string object 内部改变(添加 456 变成 123456),但 st1 pointer 保持不变并指向同一个对象。在 Swift 中,由于 String 不是可变的,var st1 必须在添加后更改其地址,因为它将保存另一个字符串,与我的字符串总和 (123456)。这一切都正确吗?

这是我用于 swift 测试的 playground 代码:

import Cocoa

var str = "123"
withUnsafePointer(to: &str) { print([=12=]) }
str += "456"
withUnsafePointer(to: &str) { print([=12=]) }

var str2 = "123"
print(Unmanaged<AnyObject>.passUnretained(str2 as AnyObject).toOpaque())
str2 += "456"
print(Unmanaged<AnyObject>.passUnretained(str2 as AnyObject).toOpaque())

这是结果:

0x000000010e228c40 // this are same
0x000000010e228c40

0x00007fb26ed5a790 // this are not
0x00007fb26ed3f6d0
// and they completly different from first two

为什么我使用 withUnsafePointer 时得到相同的指针?为什么我在使用 Unmanaged.passUnretained 时得到不同的指针?为什么从这个方法接收到的指针完全不同?

为了更好地解释您所看到的行为,我们实际上可以查看 String 源代码。

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
}

所以 String 只是某种名为 _StringCore 的类型的包装器。我们可以找到它的定义here。以下是相关部分:

public struct _StringCore {
  //...

  public var _baseAddress: UnsafeMutableRawPointer?
  var _countAndFlags: UInt

  //...
}

如您所见,_StringCore 不直接包含存储字符串内容的内存缓冲区。相反,它通过 UnsafeMutableRawPointer.

从外部引用它

第一次声明 str 时,它在堆栈上的地址 0x000000010e228c40 处获得了一些内存。当您对 str 进行更改时,您实际上对 String 结构的位置没有影响。相反,您导致 String_core_baseAddress 发生变化。 Array 的工作方式非常相似。这也是字符串的写时复制行为的实现方式。

至于 Unmanaged 行为,str2 as AnyObject 创建了 str2 的副本,因此您最终制作了 2 个不同的副本,因此存在差异 印刷地址。