簡體   English   中英

可以在swift中創建靜態分配的數組嗎?

[英]Possible to create statically allocated array in swift?

我想在swift中創建一個結構,它具有一個固定數量的小值(例如16個浮點數)作為實例數據。 要求此結構不將這些值存儲在堆上,以便結構實例的地址是實例變量的地址。 還要求這些值可以通過下標在結構內部訪問,就像數組一樣。

在C中,你只需簡單地定義這種東西:

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

使用此代碼, sizeof(Matrix4x4) == 64以及&myMatrix == &myMatrix.elements[0]; 在swift中,如果我類似地將elements變量定義為類型[Float] ,則矩陣實例僅包含指向數組的指針,因為Array<Float>實例是存儲在堆上的對象。

有沒有辦法在swift中獲得實例變量的靜態分配而不放棄類似數組的下標訪問的便利性和效率?

目前,這在“純粹的斯威夫特”中是不可能的。 從一開始就對swift-evolution郵件列表進行了長時間的討論

要求這樣的特征,例如將矩陣結構傳遞給C函數。 據我所知,這個建議很受歡迎,但目前還沒有具體的計划,也沒有在目前活躍的Swift提案中列出。

交流陣列

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]將C語義映射到Swift

Swift結構具有未指定的布局。 如果依賴於特定的布局,則應該在C中定義結構並將其導入到Swift中。

后來在那次討論中

您可以保留在C中定義的結構並將其導入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;
}

我們需要兩個變體來使正確的值語義工作(下標setter需要一個變量,下標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

以類似的方式。

正如上面的答案所暗示的那樣,你可以使用withUnsafeMutableBytes()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
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM