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 个不同的副本,因此存在差异
印刷地址。
我想看看 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 个不同的副本,因此存在差异
印刷地址。