Come creare un array di oggetti di dimensioni fisse


102

In Swift, sto cercando di creare un array di 64 SKSpriteNode. Voglio prima inizializzarlo vuoto, quindi metterei Sprites nelle prime 16 celle e nelle ultime 16 celle (simulando una partita a scacchi).

Da quello che ho capito nel documento, mi sarei aspettato qualcosa del tipo:

var sprites = SKSpriteNode()[64];

o

var sprites4 : SKSpriteNode[64];

Ma non funziona. Nel secondo caso, ricevo un errore che dice: "Gli array a lunghezza fissa non sono ancora supportati". Può essere vero? A me sembra una caratteristica di base. Ho bisogno di accedere all'elemento direttamente dal loro indice.

Risposte:


148

Gli array a lunghezza fissa non sono ancora supportati. Cosa significa in realtà? Non che tu non possa creare un array di nmolte cose - ovviamente puoi solo fare let a = [ 1, 2, 3 ]per ottenere un array di tre Ints. Significa semplicemente che la dimensione dell'array non è qualcosa che puoi dichiarare come informazioni sul tipo .

Se vuoi un array di nils, avrai prima bisogno di un array di un tipo facoltativo - [SKSpriteNode?], non [SKSpriteNode]- se dichiari una variabile di tipo non facoltativo, che si tratti di un array o di un singolo valore, non può essere nil. (Nota anche che [SKSpriteNode?]è diverso da [SKSpriteNode]?... vuoi un array di optional, non un array opzionale.)

Swift è molto esplicito in base alla progettazione nel richiedere l'inizializzazione delle variabili, perché le ipotesi sul contenuto dei riferimenti non inizializzati sono uno dei modi in cui i programmi in C (e alcuni altri linguaggi) possono diventare difettosi. Quindi, è necessario chiedere esplicitamente un [SKSpriteNode?]array che contenga 64 nils:

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

Questo in realtà restituisce un [SKSpriteNode?]?, però: un array opzionale di sprite opzionali. (Un po 'strano, poiché init(count:,repeatedValue:)non dovrebbe essere possibile restituire zero.) Per lavorare con l'array, è necessario scartarlo. Ci sono alcuni modi per farlo, ma in questo caso preferirei la sintassi di associazione facoltativa:

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

Grazie, ho provato quello, ma avevo dimenticato il "?". Tuttavia, non riesco ancora a modificare il valore? Ho provato entrambi: 1) sprite [0] = spritePawn e 2) sprites.insert (spritePawn, atIndex: 0).
Henri Lapierre

1
Sorpresa! Cmd-clic spritesnel tuo editor / playground per vedere il suo tipo dedotto - in realtà è SKSpriteNode?[]?: un array opzionale di sprite opzionali. Non puoi mettere in pedice un opzionale, quindi devi scartarlo ... vedi risposta modificata.
Rickster

Questo è davvero strano. Come hai detto, non credo che l'array debba essere opzionale, poiché lo abbiamo definito esplicitamente come? [] E non? [] ?. Un po 'fastidioso doverlo scartare ogni volta che ne ho bisogno. In ogni caso, questo sembra funzionare: var sprites = SKSpriteNode? [] (Count: 64, repeatValue: nil); se var unfrappedSprite = sprite {unfrappedSprite [0] = spritePawn; }
Henri Lapierre

La sintassi è cambiata per Swift 3 e 4, vedere altre risposte di seguito
Crashalot

61

Il meglio che sarai in grado di fare per ora è creare un array con un conteggio iniziale che si ripete zero:

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

Puoi quindi inserire i valori che desideri.


In Swift 3.0 :

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

5
c'è un modo per dichiarare un array di dimensione fissa?
ア レ ッ ク ス

2
@AlexanderSupertramp no, non c'è modo di dichiarare una dimensione per un array
drewag

1
@ ア レ ッ ク ス Non c'è modo di dichiarare una dimensione fissa per un array, ma puoi certamente creare la tua struttura che avvolge un array che impone una dimensione fissa.
pareggio il

10

Questa domanda ha già avuto risposta, ma per alcune informazioni extra al momento di Swift 4:

In caso di prestazioni, è necessario riservare memoria per l'array, in caso di crearlo dinamicamente, ad esempio aggiungendo elementi con Array.append().

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

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

Se conosci la quantità minima di elementi che aggiungerai, ma non la quantità massima, dovresti piuttosto usare array.reserveCapacity(minimumCapacity: 64).


6

Dichiarare un SKSpriteNode vuoto, quindi non sarà necessario scartare

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

7
Stai attento con questo. Riempirà l'array con la stessa istanza di quell'oggetto (ci si potrebbe aspettare istanze distinte)
Andy Hin

Ok, ma risolve anche la domanda OP, sapendo che l'array è riempito con lo stesso oggetto istanza, allora dovrai affrontarlo, senza offesa.
Carlos.V

5

Per ora, quella semanticamente più vicina sarebbe una tupla con un numero fisso di elementi.

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)

Ma questo è (1) molto scomodo da usare e (2) il layout della memoria non è definito. (almeno sconosciuto a me)


5

Swift 4

Puoi in qualche modo pensarlo come un array di oggetti contro un array di riferimenti.

  • [SKSpriteNode] deve contenere oggetti reali
  • [SKSpriteNode?] può contenere riferimenti a oggetti o nil

Esempi

  1. Creazione di un array con 64 predefinito SKSpriteNode :

    var sprites = [SKSpriteNode](repeatElement(SKSpriteNode(texture: nil),
                                               count: 64))
  2. Creazione di un array con 64 slot vuoti (aka optionals ):

    var optionalSprites = [SKSpriteNode?](repeatElement(nil,
                                          count: 64))
  3. Convertire un array di optionals in un array di oggetti (collassando [SKSpriteNode?]in [SKSpriteNode]):

    let flatSprites = optionalSprites.flatMap { $0 }

    Il valore countdel risultato flatSpritesdipende dal conteggio degli oggetti in optionalSprites: gli optionals vuoti verranno ignorati, cioè saltati.


flatMapè deprecato, dovrebbe essere aggiornato compactMapse possibile. (Non posso modificare questa risposta)
HaloZero

1

Se quello che vuoi è un array di dimensioni fisse e inizializzalo con nilvalori, puoi usare un UnsafeMutableBufferPointer, allocare memoria per 64 nodi con esso e quindi leggere / scrivere da / alla memoria indicando l'istanza del tipo di puntatore. Questo ha anche il vantaggio di evitare di controllare se la memoria deve essere riallocata, il che Arrayfa. Sarei comunque sorpreso se il compilatore non lo ottimizzasse per gli array che non hanno più chiamate a metodi che potrebbero richiedere il ridimensionamento, oltre che nel sito di creazione.

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()

Tuttavia, questo non è molto facile da usare. Quindi, facciamo un involucro!

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

Ora, questa è una classe e non una struttura, quindi ci sono alcune spese generali di conteggio dei riferimenti sostenute qui. Puoi cambiarlo in un structinvece, ma poiché Swift non ti fornisce la possibilità di usare gli inizializzatori di copia e deinitsulle strutture, avrai bisogno di un metodo di deallocazione ( func release() { memory.deallocate() }) e tutte le istanze copiate della struttura faranno riferimento alla stessa memoria.

Ora, questa classe potrebbe essere abbastanza buona. Il suo utilizzo è semplice:

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

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

for sprite in sprites {
    print(sprite!)
}

Per ulteriori protocolli a cui implementare la conformità, vedere la documentazione di Array (scorrere fino a Relationships ).


-3

Una cosa che potresti fare sarebbe creare un dizionario. Potrebbe essere un po 'sciatto considerando che stai cercando 64 elementi ma porta a termine il lavoro. Non sono sicuro che sia il "modo preferito" per farlo, ma ha funzionato per me utilizzando un array di strutture.

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

2
In che modo è migliore dell'array? Per me è un trucco che non risolve nemmeno il problema: potresti benissimo fare un tasks[65] = foosia in questo caso che nel caso di un array dalla domanda.
LaX
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.