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

时间:2014-06-24 19:51:07

标签: swift ios8 xcode6

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

根据我在文档中的理解,我会期待类似的东西:

var sprites = SKSpriteNode()[64];

var sprites4 : SKSpriteNode[64];

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

8 个答案:

答案 0 :(得分:123)

尚不支持固定长度数组。这究竟意味着什么?并不是说你不能创建n多个事物的数组 - 显然你可以let a = [ 1, 2, 3 ]来获得三个Int的数组。这意味着只是数组大小不能将声明为类型信息

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

Swift在设计上非常明确地要求对变量进行初始化,因为关于未初始化引用内容的假设是C(和其他一些语言)中的程序可能变成错误的方式之一。因此,您需要明确要求包含64 [SKSpriteNode?] s的<{1}}数组:

nil

这实际上返回var sprites = [SKSpriteNode?](repeating: nil, count: 64) :可选的精灵数组。 (有点奇怪,因为[SKSpriteNode?]?不应该返回nil。)要处理数组,你需要打开它。有几种方法可以做到这一点,但在这种情况下,我更喜欢可选的绑定语法:

init(count:,repeatedValue:)

答案 1 :(得分:56)

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

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

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


Swift 3.0

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

答案 2 :(得分: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)内存布局未定义。 (至少我不知道)

答案 3 :(得分:4)

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

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

答案 4 :(得分:4)

Swift 4

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

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

<强>实施例

  1. 使用64 默认 SKSpriteNode创建数组:

    var sprites = [SKSpriteNode](repeatElement(SKSpriteNode(texture: nil),
                                               count: 64))
    
  2. 创建一个包含64个空插槽的数组(a.k.a optionals ):

    var optionalSprites = [SKSpriteNode?](repeatElement(nil,
                                          count: 64))
    
  3. 将一个选项数组转换为一个对象数组(将[SKSpriteNode?]折叠为[SKSpriteNode]):

    let flatSprites = optionalSprites.flatMap { $0 }
    

    结果count的{​​{1}}取决于flatSprites中对象的数量:空的选项将被忽略,即跳过。

答案 5 :(得分:3)

这个问题已经得到解答,但是对于Swift 4时的一些额外信息:

如果是性能,您应该为数组保留内存,以便动态创建它,例如添加Array.append()元素。

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

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

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

答案 6 :(得分: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;
    }
}

现在,这是一个类,而不是结构,因此这里有一些引用计数开销。您可以改为将其更改为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 documentation(滚动到 Relationships )。

答案 7 :(得分:-2)

你可以做的一件事就是创建一本字典。考虑到你正在寻找64个元素,它可能会有点草率,但它可以完成工作。我不确定它是否是首选方式&#34;要做到这一点,但它使用一系列结构对我有用。

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