我有一些代码开始生成有关我在 Swift 5.10(可能更早)中使用
&
运算符的警告。
将“UnsafeMutableRawPointer”形成为“Optional”类型的变量;这可能是不正确的,因为“可选”可能包含对象引用。
public func bad(audioObject: AudioObjectID) {
var propAddr = AudioObjectPropertyAddress()
var x: CFString!
var size = UInt32(MemoryLayout.size(ofValue: x))
let _ = AudioObjectGetPropertyData(audioObject, &propAddr, 0, nil, &size, &x)
// ^
// Forming 'UnsafeMutableRawPointer' to a variable of type 'Optional<CFString>'; this is likely incorrect because 'Optional<CFString>' may contain an object reference.
}
所以我机械地将代码转换为不产生该警告,但我不明白有什么区别,也不明白原始版本是否确实隐藏了错误。
func good(audioObject: AudioObjectID) {
var propAddr = AudioObjectPropertyAddress()
var x: CFString!
var size = UInt32(MemoryLayout.size(ofValue: x))
let _ = withUnsafeMutablePointer(to: &x) { pointerToX in
return AudioObjectGetPropertyData(audioObject, &propAddr, 0, nil, &size, pointerToX)
}
}
那么“坏”版本“可能”出了什么问题,为什么“好”版本没有问题?
添加的。当您将非平凡类型隐式转换为原始指针时,会发出此警告。提交消息显示:
例如,支持这种转换结果很糟糕:
void read_void(const void *input); func foo(data: inout Data) { read_void(&data) }
人们期望 Foundation.Data 具有相同的类型是可以理解的 隐式转换为数组。但它做了一些非常错误的事情 相反。这个想法是,人们经常误解这个
&
转换的作用,并使用它来调用直接从具有非平凡类型的指针读取数据的函数。例如,在上面的示例中,
read_void(&data)
不会将指针传递到
Data
所保存的实际缓冲区(在堆中)。这样形成的指针只会指向栈上的参数。 Data
结构本身包含一个指向实际缓冲区的指针,应该使用
Data.withUnsafeBytes
来获取该指针并将其传递给
read_void
。
这就是错误消息提到“可能包含对象引用”的原因。它本质上是说“对于您实际想要传递的数据可能还有进一步的间接层”。当然,以
CFString
AudioObjectGetPropertyData
也知道它在做什么。您知道原始指针不会像缓冲区一样被读取,并且该函数只会向其写入
CFString
。
通过使用
withUnsafeMutablePointer
,您可以更清楚地表达这一意图。您明确表示您想要一个指向堆栈上 CFString
值的指针,而不是其内容。