如何创建一个固定大小的对象数组

在 Swift 中,我试图创建一个包含64个 SKSpriteNode 的数组。我想首先将其初始化为空,然后将 Sprites 放入前16个单元格,最后16个单元格(模拟象棋游戏)。

From what I understood in the doc, I would have expect something like:

var sprites = SKSpriteNode()[64];

或者

var sprites4 : SKSpriteNode[64];

但是没用。 在第二种情况下,我得到一个错误: “不支持固定长度的数组”。这是真的吗?对我来说,这听起来像是一个基本特征。 我需要通过它们的索引直接访问元素。

153307 次浏览

目前您能做的最好的事情就是创建一个初始计数为零的数组:

var sprites = [SKSpriteNode?](count: 64, repeatedValue: nil)

然后您可以填写任何您想要的值。


Swift 3.0:

var sprites = [SKSpriteNode?](repeating: nil, count: 64)

还不支持固定长度的数组。那到底是什么意思?并不是说你不能创建一个包含 n的数组,很多东西ーー显然你只需要做 let a = [ 1, 2, 3 ]就可以得到一个包含三个 Int的数组。它仅仅意味着数组大小不是可以声明为 as type information的内容。

如果你想要一个 nil的数组,你首先需要一个可选类型的数组ーー [SKSpriteNode?],而不是 [SKSpriteNode]ーー如果你声明一个非可选类型的变量,不管它是一个数组还是一个值,它都不能是 nil。(还要注意,[SKSpriteNode?][SKSpriteNode]?不同... ... 您需要一个可选数组,而不是一个可选数组。)

Swift 在设计上非常明确地要求初始化变量,因为对未初始化引用内容的假设是 C 语言(和其他一些语言)中的程序可能出错的方式之一。因此,您需要显式地请求一个包含64个 nil[SKSpriteNode?]数组:

var sprites = [SKSpriteNode?](repeating: nil, count: 64)

This actually returns a [SKSpriteNode?]?, though: an optional array of optional sprites. (A bit odd, since init(count:,repeatedValue:) shouldn't be able to return nil.) To work with the array, you'll need to unwrap it. There's a few ways to do that, but in this case I'd favor optional binding syntax:

if var sprites = [SKSpriteNode?](repeating: nil, count: 64){
sprites[0] = pawnSprite
}

One thing you could do would be to create a dictionary. Might be a little sloppy considering your looking for 64 elements but it gets the job done. Im not sure if its the "preferred way" to do it but it worked for me using an array of structs.

var tasks = [0:[forTasks](),1:[forTasks](),2:[forTasks](),3:[forTasks](),4:[forTasks](),5:[forTasks](),6:[forTasks]()]

声明一个空的 SKSpriteNode,这样就不需要展开

var sprites = [SKSpriteNode](count: 64, repeatedValue: SKSpriteNode())

目前,语义上最接近的元素是具有固定数量元素的元组。

typealias buffer = (
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode,
SKSpriteNode, SKSpriteNode, SKSpriteNode, SKSpriteNode)

但这是(1)非常不舒服的使用和(2)内存布局是未定义的。(至少我不知道)

这个问题已经得到了回答,但是在 Swift 4上还有一些额外的信息:

在性能方面,应该为数组保留内存,以便动态创建数组,例如使用 Array.append()添加元素。

var array = [SKSpriteNode]()
array.reserveCapacity(64)


for _ in 0..<64 {
array.append(SKSpriteNode())
}

如果知道要添加的元素的最小数量,但不知道最大数量,那么应该使用 array.reserveCapacity(minimumCapacity: 64)

Swift 4

你可以把它看作是对象数组和引用数组。

  • [SKSpriteNode]必须包含实际的对象
  • [SKSpriteNode?]可以包含对对象的引用,也可以包含对 nil的引用

例子

  1. 创建一个64 违约 SKSpriteNode的数组:

    var sprites = [SKSpriteNode](repeatElement(SKSpriteNode(texture: nil),
    count: 64))
    
  2. Creating an array with 64 empty slots (a.k.a optionals):

    var optionalSprites = [SKSpriteNode?](repeatElement(nil,
    count: 64))
    
  3. Converting an array of optionals into an array of objects (collapsing [SKSpriteNode?] into [SKSpriteNode]):

    let flatSprites = optionalSprites.flatMap { $0 }
    

    产生的 flatSpritescount取决于 optionalSprites中对象的计数: 空选项将被忽略,即跳过。

If what you want is a fixed size array, and initialize it with nil values, you can use an UnsafeMutableBufferPointer, allocate memory for 64 nodes with it, and then read/write from/to the memory by subscripting the pointer type instance. This also has the benefit of avoiding checking if the memory must be reallocated, which Array does. I would however be surprised if the compiler doesn't optimize that away for arrays that don't have any more calls to methods that may require resizing, other than at the creation site.

let count = 64
let sprites = UnsafeMutableBufferPointer<SKSpriteNode>.allocate(capacity: count)


for i in 0..<count {
sprites[i] = ...
}


for sprite in sprites {
print(sprite!)
}


sprites.deallocate()

然而,这并不是非常友好的用户界面。因此,让我们做一个包装!

class ConstantSizeArray<T>: ExpressibleByArrayLiteral {
    

typealias ArrayLiteralElement = T
    

private let memory: UnsafeMutableBufferPointer<T>
    

public var count: Int {
get {
return memory.count
}
}
    

private init(_ count: Int) {
memory = UnsafeMutableBufferPointer.allocate(capacity: count)
}
    

public convenience init(count: Int, repeating value: T) {
self.init(count)
        

memory.initialize(repeating: value)
}
    

public required convenience init(arrayLiteral: ArrayLiteralElement...) {
self.init(arrayLiteral.count)
        

memory.initialize(from: arrayLiteral)
}
    

deinit {
memory.deallocate()
}
    

public subscript(index: Int) -> T {
set(value) {
precondition((0...endIndex).contains(index))
            

memory[index] = value;
}
get {
precondition((0...endIndex).contains(index))
            

return memory[index]
}
}
}


extension ConstantSizeArray: MutableCollection {
public var startIndex: Int {
return 0
}
    

public var endIndex: Int {
return count - 1
}
    

func index(after i: Int) -> Int {
return i + 1;
}
}

现在,这是一个类,而不是一个结构,所以这里有一些引用计数开销。您可以将其改为 struct,但是由于 Swift 没有为您提供在结构上使用复制初始化器和 deinit的能力,因此您将需要一个释放方法(func release() { memory.deallocate() }) ,并且结构的所有复制实例都将引用相同的内存。

现在,这个类可能已经足够好了,它的用法很简单:

let sprites = ConstantSizeArray<SKSpriteNode?>(count: 64, repeating: nil)


for i in 0..<sprites.count {
sprite[i] = ...
}


for sprite in sprites {
print(sprite!)
}

For more protocols to implement conformance to, see the 数组文档 (scroll to 关系).