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


101

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

根据我在文档中了解的内容,我期望会出现以下情况:

var sprites = SKSpriteNode()[64];

要么

var sprites4 : SKSpriteNode[64];

但这是行不通的。在第二种情况下,我收到一条错误消息:“尚不支持定长数组”。那可以是真的吗?对我来说,这听起来像一个基本功能。我需要直接通过其索引访问元素。

Answers:


147

尚不支持定长数组。这实际上是什么意思?并不是说您不能创建包含n很多东西的数组-显然,您可以做的只是let a = [ 1, 2, 3 ]获得三个IntS 的数组。这仅意味着数组大小不是您可以声明为类型信息的内容

如果需要nils 的数组,则首先需要一个可选类型的数组— [SKSpriteNode?]而不是[SKSpriteNode]—如果您声明非可选类型的变量,无论是数组还是单个值,都不能为nil。(还要注意,[SKSpriteNode?][SKSpriteNode]?... 不同的是,您需要一个可选数组,而不是一个可选数组。)

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

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

[SKSpriteNode?]?但是,实际上返回一个:可选sprite的可选数组。(有点奇怪,因为init(count:,repeatedValue:)不应该返回nil。)要使用数组,您需要将其拆开。有几种方法可以做到这一点,但在这种情况下,我倾向于使用可选的绑定语法:

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

谢谢,我确实尝试过那个,但我忘记了“?”。但是,我仍然无法更改值?我都尝试过:1)sprites [0] = spritePawn和2)sprites.insert(spritePawn,atIndex:0)。
亨利·拉皮埃尔

1
惊喜!sprites在您的编辑器/游乐场中单击Cmd ,以查看其推断的类型-实际上是SKSpriteNode?[]?:可选sprite的可选数组。您无法为可选内容添加下标,因此必须将其拆开...请参见编辑后的答案。
rickster

确实很奇怪。正如您提到的,我认为数组不应该是可选的,因为我们将其显式定义为?[]而不是?[]?。每当我需要它时,都需要将其拆开。无论如何,这似乎可行:var sprites = SKSpriteNode?[](count:64,repeatedValue:nil); 如果var unwrappedSprite = sprites {unwrappedSprite [0] = spritePawn; }
Henri Lapierre 2014年

Swift 3和4的语法已更改,请参见下面的其他答案
Crashalot

61

您现在要做的最好的事情是创建一个初始计数重复nil的数组:

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

然后,您可以填写所需的任何值。


Swift 3.0中

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

5
有什么方法可以声明固定大小的数组?
アレックス

2
@AlexanderSupertramp不,没有办法声明数组的大小
drewag 2015年

1
@araレクス无法为数组声明固定大小,但是您当然可以创建自己的结构,该结构将强制固定大小的数组包装起来。
drewag '16

10

这个问题已经得到解答,但是在Swift 4发行时,还有一些其他信息:

为了提高性能,您应该为数组保留内存,以动态创建它,例如使用添加元素Array.append()

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

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

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


6

声明一个空的SKSpriteNode,因此不需要拆包

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

7
请注意这一点。它将使用该对象的相同实例填充数组(可能会有不同的实例)
Andy Hin

好的,但是它解决了OP问题,并且知道数组中填充了相同的实例对象,那么您将不得不对其进行处理,这没有任何冒犯。
卡洛斯·V

5

就目前而言,在语义上最接近的一个将是具有固定数量元素的元组。

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)内存布局未定义。(至少对我来说是未知的)


5

斯威夫特4

您可以将其视为对象数组与引用数组。

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

例子

  1. 创建一个默认 值为64的数组SKSpriteNode

    var sprites = [SKSpriteNode](repeatElement(SKSpriteNode(texture: nil),
                                               count: 64))
  2. 创建一个具有64个空插槽的数组(又名可选):

    var optionalSprites = [SKSpriteNode?](repeatElement(nil,
                                          count: 64))
  3. 将可选数组转换为对象数组([SKSpriteNode?]合并为[SKSpriteNode]):

    let flatSprites = optionalSprites.flatMap { $0 }

    count由此而来的flatSprites依赖于对象的个数optionalSprites:空自选项目都将被忽略,即跳过。


flatMap不建议使用,compactMap如果可能,应将其更新为。(我无法编辑此答案)
HaloZero

1

如果要的是固定大小的数组,并使用nil值对其进行初始化,则可以使用UnsafeMutableBufferPointer,为其分配64个节点的内存,然后通过对指针类型实例进行下标来对内存进行读/写操作。这还具有避免检查是否必须重新分配内存的好处Array。但是,如果编译器没有针对除创建站点之外不再需要调用可能需要重新调整大小的方法的数组进行优化,我会感到惊讶。

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;
    }
}

现在,这是一个类,而不是结构,所以这里有一些引用计数开销。您可以将其更改为a 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!)
}

有关实现一致性的更多协议,请参见Array文档(滚动至Relationships)。


-3

您可以做的一件事就是创建字典。考虑到您正在寻找64个元素,可能会有些草率,但可以完成工作。我不确定它是否是“首选方式”,但使用结构数组对我有用。

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

2
那比数组好吗?对我来说,这是一个无法解决问题的技巧:tasks[65] = foo在这种情况下以及问题数组中的情况下,您都可以做到。
LaX
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.