C API 在 Swift 中具有结构 - 不可变值作为 inout 参数



我正在做一个 Swift 3 项目,该项目涉及使用一些我从 Objective-C 桥接的 C API。

下面是 API 结构的示例片段:

typedef struct
{
StructMode     mode;
StructLevel    level;
} TargetStruct;
typedef struct
{
. . . 
TargetStruct        *targetStruct;
OtherStruct         *otherStruct;
NonPointerStructA   nonPointerStructA;
NonPointerStructB   nonPointerStructB;
. . .
} InnerStruct;
typedef struct
{
InnerStruct     innerStruct;
OtherStructB    otherStructB;
} OuterStruct;

在我的 Swift 代码中,我的目标是从 OuterStruct 中设置 TargetStruct 的值,如下所示:

// run function that returns an instance of TargetStruct
var targetStruct: TargetStruct = initializeTargetStruct()
// assign targetStruct to outerStruct
outerStruct.innerStruct.targetStruct = &targetStruct

但是,我收到以下错误:

Cannot pass immutable value of TargetStruct as inout argument

如果我设置一个没有 * 的结构值,它将正常工作:

var nonPointerStructA: NonPointerStructA = initializeNonPointerStructA()
outerStruct.innerStruct.nonPointerStructA = nonPointerStructA

我尝试像这样设置 targetStruct 的值,但现在我无法测试它:

var targetStruct: TargetStruct = initializeTargetStruct()
outerStruct.innerStruct.targetStruct.initialize(from: &targetStruct, count: 0)

如何解决这个问题?谢谢。

在 Swift 中,前缀&不是地址运算符。只需要澄清一些表达式被传递给inout参数。因此,您的第一个代码在 Swift 中语法无效。

你的 C 结构体被导入到 Swift 中,如下所示:

struct TargetStruct {
var mode: StructMode
var level: StructLevel
//some auto generated initializers...
}
struct InnerStruct {
//...
var targetStruct: UnsafeMutablePointer<TargetStruct>!
var otherStruct: UnsafeMutablePointer<OtherStruct>!
var nonPointerStructA: NonPointerStructA
var nonPointerStructB: NonPointerStructB
//some auto generated initializers...
}
struct OuterStruct {
var innerStruct: InnerStruct
var otherStructB: OtherStructB
//some auto generated initializers...
}

(如果有什么问题,请告诉我。

如您所见,InnerStruct中的targetStruct是一个指针,initialize(from:count:)尝试写入指向的区域,但是在您调用initialize(from:count:)时,targetStruct保持其初始值nil,您知道取消引用空指针时会发生什么。


一种方法是为TargetStruct分配内存,并使用指向已分配区域的指针。

func allocateAndInitializeTargetStruct() -> UnsafeMutablePointer<TargetStruct> {
let targetStructRef = UnsafeMutablePointer<TargetStruct>.allocate(capacity: 1)
targetStructRef.initialize(to: initializeTargetStruct())
return targetStructRef
}
outerStruct.innerStruct.targetStruct = allocateAndInitializeTargetStruct()

这是比下面更通用的方法,但您需要显式deinitializedeallocate分配的区域。这有点难以管理。


如果可以将outerStruct的使用限制在单个代码块中,则可以编写如下内容:

var targetStruct = initializeTargetStruct()
withUnsafeMutablePointer(to: &targetStruct) {targetStructPtr in
outerStruct.innerStruct.targetStruct = targetStructPtr
//Use `outerStruct` only inside this code-block
//...
}

在这种情况下,outerStruct.innerStruct.targetStruct(==targetStructPtr( 中持有的指针仅在闭包内部有效,您不能在闭包外部使用它。


如果上述任何代码不适合您的用例,您可能需要提供更多上下文以找到最佳解决方案。


嵌套使用withUnsafeMutablePointer(to:_:)的示例:

var targetStruct = initializeTargetStruct()
var otherStruct = initializeOtherStruct()
withUnsafeMutablePointer(to: &targetStruct) {targetStructPtr in
withUnsafeMutablePointer(to: &otherStruct) {otherStructPtr in
outerStruct.innerStruct.targetStruct = targetStructPtr
outerStruct.innerStruct.otherStruct = otherStructPtr
//Use `outerStruct` only inside this code-block
//...
}
}

当你需要设置更多指针时,这种嵌套会一团糟,但这是 Swift 当前的限制。


deinitializedeallocate的示例:

extension InnerStruct {
func freeMemberStructs() {
if let targetStructRef = targetStruct {
targetStructRef.deinitialize()
targetStructRef.deallocate(capacity: 1)
targetStruct = nil
}
if let otherStructRef = otherStruct {
otherStructRef.deinitialize()
otherStructRef.deallocate(capacity: 1)
otherStruct = nil
}
}
}
outerStruct.innerStruct.targetStruct = allocateAndInitializeTargetStruct()
outerStruct.innerStruct.otherStruct = allocateAndInitializeOtherStruct()
// Use `outerStruct`
//...
outerStruct.innerStruct.freeMemberStructs()

代码可能看起来不太复杂(只是一堆样板代码(,但很难找到何时何地执行此操作。由于您的InnerStruct可能嵌入在另一个结构中,因此可能需要deinitilized 和deallocated...

希望您能找到最佳解决方案。

最新更新