Sabit boyutlu nesne dizisi oluşturma


102

Swift'de 64 SKSpriteNode dizisi oluşturmaya çalışıyorum. Önce boş olarak başlatmak istiyorum, sonra Sprites'i ilk 16 hücreye ve son 16 hücreye (bir satranç oyununu simüle ederek) koyacağım.

Belgeden anladığım kadarıyla şöyle bir şey beklerdim:

var sprites = SKSpriteNode()[64];

veya

var sprites4 : SKSpriteNode[64];

Ama işe yaramıyor. İkinci durumda, "Sabit uzunluklu diziler henüz desteklenmiyor" şeklinde bir hata alıyorum. Bu gerçek olabilir mi? Bana göre bu temel bir özellik gibi geliyor. Öğeye doğrudan indekslerinden erişmem gerekiyor.

Yanıtlar:


152

Sabit uzunluklu diziler henüz desteklenmemektedir. Bu aslında ne anlama geliyor? Pek nçok şeyden oluşan bir dizi oluşturamayacağınızdan değil - tabii ki sadece let a = [ 1, 2, 3 ]üç Ints'lik bir dizi elde etmek için yapabilirsiniz . Basitçe, dizi boyutunun tür bilgisi olarak tanımlayabileceğiniz bir şey olmadığı anlamına gelir .

Bir dizi dizisi istiyorsanız nil, önce isteğe bağlı bir türden bir diziye ihtiyacınız olacak - [SKSpriteNode?]değil [SKSpriteNode]- isteğe bağlı olmayan türde bir değişken bildirirseniz, ister bir dizi ister tek bir değer, olamaz nil. (Ayrıca unutmayın [SKSpriteNode?]farklıdır [SKSpriteNode]?Eğer seçeneklere bir dizi değil, isteğe bağlı bir dizi istiyorum ....)

Swift, değişkenlerin başlatılmasını zorunlu kılma konusunda tasarım gereği çok açıktır, çünkü başlatılmamış referansların içeriği hakkındaki varsayımlar, C'deki (ve diğer bazı dillerdeki) programların hatalı hale gelmesinin yollarından biridir. Bu nedenle, açıkça [SKSpriteNode?]64 nils içeren bir dizi istemeniz gerekir :

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

Bu aslında [SKSpriteNode?]?, bir isteğe bağlı hareketli karakter dizisi döndürür . (Biraz tuhaf, çünkü init(count:,repeatedValue:)sıfıra dönmemeli.) Diziyle çalışmak için onu açmanız gerekecek. Bunu yapmanın birkaç yolu var, ancak bu durumda isteğe bağlı bağlama sözdizimini tercih ederim:

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

Teşekkürler, bunu denedim ama "?" Ancak yine de değeri değiştiremiyorum? Her ikisini de denedim: 1) sprite [0] = spritePawn ve 2) sprites.insert (spritePawn, atIndex: 0).
Henri Lapierre

1
Sürpriz! Çıkarılmış spritestürünü görmek için editörünüze / oyun alanınıza Cmd-tıklayın - aslında SKSpriteNode?[]?: isteğe bağlı bir dizi isteğe bağlı sprite. İsteğe bağlı bir abone olamazsınız, bu yüzden onu açmanız gerekir ... düzenlenen cevaba bakın.
rickster

Bu gerçekten çok garip. Bahsettiğiniz gibi, açık bir şekilde? [] Ve? []? Olarak tanımladığımız için dizinin isteğe bağlı olması gerektiğini düşünmüyorum. Her ihtiyacım olduğunda paketini açmak zorunda kalmak biraz can sıkıcı. Her durumda, bu işe yarıyor gibi görünüyor: var sprites = SKSpriteNode? [] (Sayı: 64, yinelenenDeğer: nil); eğer var wrappedSprite = sprites {awrappedSprite [0] = spritePawn; }
Henri Lapierre

Swift 3 ve 4 için sözdizimi değişti, lütfen aşağıdaki diğer yanıtlara bakın
Crashalot

62

Şimdilik yapabileceğiniz en iyi şey, başlangıç ​​sayısının sıfır ile tekrarlandığı bir dizi oluşturmaktır:

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

Daha sonra istediğiniz değerleri girebilirsiniz.


In Swift 3.0 :

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

5
sabit boyutta bir dizi bildirmenin herhangi bir yolu var mı?
ア レ ッ ク ス

2
@AlexanderSupertramp hayır, bir dizi için boyut bildirmenin bir yolu yok
drewag

1
@ ア レ ッ ク ス Bir dizi için sabit bir boyut bildirmenin bir yolu yoktur, ancak kesinlikle sabit bir boyutu zorlayan bir diziyi saran kendi yapınızı yaratabilirsiniz.
2016

11

Bu soru zaten cevaplandı, ancak Swift 4 sırasında bazı ekstra bilgiler için:

Performans durumunda, dinamik olarak oluşturma durumunda, örneğin ile eleman eklemek gibi, dizi için bellek ayırmalısınız Array.append().

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

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

Ekleyeceğiniz minimum öğe miktarını biliyorsanız, ancak maksimum miktarı bilmiyorsanız, kullanmayı tercih etmelisiniz array.reserveCapacity(minimumCapacity: 64).


6

Boş bir SKSpriteNode bildirin, böylece açmaya gerek kalmaz

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

9
Buna dikkat et. Diziyi bu nesnenin aynı örneğiyle dolduracak (farklı örnekler beklenebilir)
Andy Hin

Tamam, ancak OP sorusunu çözüyor, ayrıca dizinin aynı nesne nesnesiyle dolu olduğunu bilerek, onunla uğraşmanız gerekecek, suç yok.
Carlos.V

5

Şimdilik, anlamsal olarak en yakın olan sabit sayıda eleman içeren bir demet olacaktır.

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)

Ancak bu (1) kullanımı çok rahatsız edici ve (2) bellek düzeni tanımlanmadı. (en azından benim için bilinmiyor)


5

Swift 4

Bunu bir şekilde nesne dizisi ve referans dizisi olarak düşünebilirsiniz.

  • [SKSpriteNode] gerçek nesneler içermelidir
  • [SKSpriteNode?] nesnelere referanslar içerebilir veya nil

Örnekler

  1. 64 varsayılan ile bir dizi oluşturma SKSpriteNode:

    var sprites = [SKSpriteNode](repeatElement(SKSpriteNode(texture: nil),
                                               count: 64))
    
  2. 64 boş yuvaya sahip bir dizi oluşturma (diğer adıyla isteğe bağlı ):

    var optionalSprites = [SKSpriteNode?](repeatElement(nil,
                                          count: 64))
    
  3. Bir isteğe bağlı diziyi bir nesneler dizisine [SKSpriteNode?]dönüştürme ( içine daraltma [SKSpriteNode]):

    let flatSprites = optionalSprites.flatMap { $0 }
    

    Sonuç count, içindeki flatSpritesnesnelerin sayısına bağlıdır optionalSprites: boş opsiyonlar dikkate alınmaz, yani atlanır.


flatMapkullanımdan kaldırıldı, mümkünse olarak güncellenmesi gerekiyor compactMap. (Bu yanıtı düzenleyemiyorum)
HaloZero

1

İstediğiniz şey sabit boyutlu bir diziyse ve onu nildeğerlerle başlatırsanız , bir kullanabilir UnsafeMutableBufferPointer, onunla 64 düğüm için bellek ayırabilir ve ardından işaretçi türü örneğini abone olarak bellekten / bellekten okuyabilir / yazabilirsiniz. Bu aynı zamanda, belleğin yeniden tahsis edilmesi gerekip gerekmediğini kontrol etmekten kaçınma avantajına da sahiptir, bu da Arrayyapar. Bununla birlikte, derleyici, oluşturma sitesi dışında yeniden boyutlandırma gerektirebilecek yöntemlere daha fazla çağrı içermeyen diziler için bunu optimize etmezse şaşırırdım.

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

Ancak bu pek kullanıcı dostu değildir. Öyleyse bir sarmalayıcı yapalım!

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

Şimdi, bu bir sınıf ve bir yapı değil, bu yüzden burada bazı referans sayma ek yükleri var. Bunun structyerine bir olarak değiştirebilirsiniz , ancak Swift size kopya başlatıcıları ve deinityapıları kullanma yeteneği sağlamadığından , bir serbest bırakma yöntemine ( func release() { memory.deallocate() }) ihtiyacınız olacak ve yapının tüm kopyalanan örnekleri aynı belleğe başvuracaktır.

Şimdi, bu ders yeterince iyi olabilir. Kullanımı basittir:

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

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

for sprite in sprites {
    print(sprite!)
}

Uyumluluğu uygulayabileceğiniz daha fazla protokol için Dizi belgelerine bakın ( İlişkiler bölümüne gidin ).


-3

Yapabileceğiniz tek şey bir sözlük oluşturmak olabilir. 64 element aradığınız düşünüldüğünde biraz özensiz olabilir, ancak işi halleder. Bunu yapmanın "tercih edilen yolu" olup olmadığından emin değilim ama bir dizi yapı kullanarak benim için çalıştı.

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

2
Bu nasıl diziden daha iyi? Bana göre bu, sorunu çözmeyen bir hack: tasks[65] = foohem bu durumda hem de sorudan bir dizi durumunda çok iyi bir şekilde a yapabilirsiniz .
LaX
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.