目前,这在“纯 Swift”中是不可能的。有一个很长的讨论
在 swift-evolution 邮件列表中,起始地址为
- [swift-evolution] 提案:连续变量(又名固定大小数组类型) https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/007984.html
它需要这样的功能,例如将矩阵结构传递给 C 函数。
据我所知,这个建议很受欢迎,但没有具体的计划
截至目前,它还没有被列入当前有效的 Swift 提案 https://github.com/apple/swift-evolution/tree/master/proposals.
C 数组
float elements[16];
作为包含 16 个组件的元组导入到 Swift 中:
public var elements: (Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float, Float)
目前,这似乎是定义具有给定内存布局的固定大小结构的唯一方法。
Apple 的 Joe Groff 撰文于[swift-user] 将 C 语义映射到 Swift https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20160516/001968.html
Swift 结构具有未指定的布局。如果您依赖于特定的布局,您现在应该在 C 中定义该结构并将其导入到 Swift 中。
and 后来在那次讨论中 https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20160516/001980.html:
您可以保留在 C 中定义的结构并将其导入到 Swift 中。 Swift 将尊重 C 的布局。
如果矩阵类型是在 C 头文件中定义的(为了简单起见,我使用
现在以 2x2 矩阵为例)
// matrix.h:
typedef struct Matrix2x2 {
float elements[4];
} Matrix2x2;
然后将其导入到 Swift 中:
public struct Matrix2x2 {
public var elements: (Float, Float, Float, Float)
public init()
public init(elements: (Float, Float, Float, Float))
}
如上所述,Swift 保留了 C 内存布局,因此矩阵及其
元素和第一个元素都具有相同的地址:
var mat = Matrix2x2(elements: (1, 2, 3, 4))
print(sizeofValue(mat)) // 16
withUnsafePointer(&mat) { print($0) } // 0x00007fff5fbff808
withUnsafePointer(&mat.elements) { print($0) } // 0x00007fff5fbff808
withUnsafePointer(&mat.elements.0) { print($0) } // 0x00007fff5fbff808
然而,元组是不可下标的,如果元组成员具有
不同种类。 swift-evolution 邮件列表上还有另一个讨论
- [swift-evolution] 统一元组上的 CollectionType [分叉连续变量] https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009682.html
将“统一元组”视为集合,这将允许下标。
不幸的是,这尚未实施。
有一些方法可以通过索引访问元组成员,例如使用Mirror()
or withUnsafe(Mutable)Pointer()
.
这是一个可能的解决方案斯威夫特 3 (Xcode 8),这似乎运作良好
并且只涉及很少的开销。 “技巧”是定义 C 函数
返回指向元素存储的指针:
// matrix.h:
// Constant pointer to the matrix elements:
__attribute__((swift_name("Matrix2x2.pointerToElements(self:)")))
static inline const float * _Nonnull matrix2x2PointerToElements(const Matrix2x2 * _Nonnull mat)
{
return mat->elements;
}
// Mutable pointer to the matrix elements:
__attribute__((swift_name("Matrix2x2.pointerToMutableElements(self:)")))
static inline float * _Nonnull pointerToMutableElements(Matrix2x2 * _Nonnull mat)
{
return mat->elements;
}
我们需要两个变体来使正确的值语义发挥作用(下标设置器需要
变量、下标 getter 适用于常量或变量)。
“swift_name”属性使编译器将这些函数导入为成员
的职能Matrix2x2
类型、比较
- SE-0044 作为会员导入 https://github.com/apple/swift-evolution/blob/master/proposals/0044-import-as-member.md
现在我们可以在 Swift 中定义下标方法:
extension Matrix2x2 {
public subscript(idx: Int) -> Float {
get {
precondition(idx >= 0 && idx < 4)
return pointerToElements()[idx]
}
set(newValue) {
precondition(idx >= 0 && idx < 4)
pointerToMutableElements()[idx] = newValue
}
}
}
一切都按预期进行:
// A constant matrix:
let mat = Matrix2x2(elements: (1, 2, 3, 4))
print(mat[0], mat[1], mat[2], mat[3]) // 1.0 2.0 3.0 4.0
// A variable copy:
var mat2 = mat
mat2[0] = 30.0
print(mat2) // Matrix2x2(elements: (30.0, 2.0, 3.0, 4.0))
当然你也可以定义类似矩阵的下标方法
public subscript(row: Int, col: Int) -> Float
以类似的方式。