【问题标题】:Possible to create statically allocated array in swift?可以快速创建静态分配的数组吗?
【发布时间】:2016-11-12 22:18:29
【问题描述】:

我想在 swift 中创建一个结构,它有少量固定值(比如 16 个浮点数)作为实例数据。要求该结构不要将这些值存储在堆上,这样结构的实例地址就是实例变量的地址。这也是一个要求,这些值可以通过下标在结构内部访问,就像数组一样。

在 C 中,你可以简单地定义这种东西:

struct Matrix4x4 {
    float elements[16];
    ...
} myMatrix;

使用此代码,sizeof(Matrix4x4) == 64 和 &myMatrix == &myMatrix.elements[0]; 在 swift 中,如果我类似地将 elements 变量定义为类型 [Float],矩阵实例仅包含指向数组的指针,因为 Array<Float> 实例是一个存储在堆上的对象。

有没有办法在不放弃类似数组的下标访问的便利性和效率的情况下快速获得实例变量的静态分配?

【问题讨论】:

    标签: arrays swift static-allocation


    【解决方案1】:

    目前,这在“纯 Swift”中是不可能的。有很长的讨论 在 swift-evolution 邮件列表中,从

    开始

    要求这样的功能,例如将矩阵结构传递给 C 函数。 据我所知,这个建议很受欢迎,但没有具体计划 截至目前,并没有列入 currently active Swift proposals.

    A 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-users] Mapping C semantics to Swift

    Swift 结构具有未指定的布局。如果你依赖于一个特定的布局,你现在应该在 C 中定义结构并将其导入到 Swift 中。

    和later in that discussion:

    您可以保留在 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 邮件列表还有另一个讨论

    将“统一元组”视为集合,这将允许下标。 不幸的是,这还没有实现。

    有一些方法可以通过索引访问元组成员,例如使用Mirror() 或withUnsafe(Mutable)Pointer()。

    这是 Swift 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 类型的函数,比较

    现在我们可以在 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
    

    以类似的方式。

    【讨论】:

      【解决方案2】:

      正如上面的答案所暗示的,您可以使用 withUnsafeMutableBytes() 和 assumingMemoryBound(to:) 的组合将 C 数组视为调用范围内的 swift 数组。

      withUnsafeMutableBytes(of: &mymatrix.elements) { rawPtr in
              let floatPtr = rawPtr.baseAddress!.assumingMemoryBound(to: Float.self)
              // Use the floats (with no bounds checking)
              // ...
              for i in 0..<10 {
                  floatPtr[i] = 42.0
              }
          }
      

      【讨论】:

        猜你喜欢
        • 2010-12-13
        • 1970-01-01
        • 1970-01-01
        • 2011-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-14
        相关资源
        最近更新 更多