[swift] 고정 된 크기의 객체 배열을 만드는 방법

Swift에서 64 SKSpriteNode 배열을 만들려고합니다. 먼저 비워두고 초기화 한 다음 처음 16 개 셀에 스프라이트를, 마지막 16 개 셀 (체스 게임 시뮬레이션)에 넣습니다.

내가 문서에서 이해 한 바에 따르면 다음과 같은 것을 기대했을 것입니다.

var sprites = SKSpriteNode()[64];

또는

var sprites4 : SKSpriteNode[64];

하지만 작동하지 않습니다. 두 번째 경우 “고정 길이 배열은 아직 지원되지 않습니다.”라는 오류가 발생합니다. 그게 진짜일까요? 나에게 그것은 기본 기능처럼 들립니다. 색인으로 직접 요소에 액세스해야합니다.



답변

고정 길이 배열은 아직 지원되지 않습니다. 이것은 실제로 무엇을 의미합니까? n많은 것의 배열을 만들 수 없다는 것이 아닙니다. 분명히 let a = [ 1, 2, 3 ]3 개의 배열을 얻기 위해 할 수 있습니다 Int. 이는 단순히 배열 크기가 유형 정보로 선언 할 수있는 것이 아님을 의미합니다 .

당신의 배열하려면 nil– S를 먼저 선택 유형의 배열을해야 [SKSpriteNode?]하지 [SKSpriteNode]– 당신이 배열 또는 단일 값인지, 비 선택적인 유형의 변수를 선언하는 경우, 그것은 될 수 없습니다 nil. (또한 옵션 배열이 아닌 옵션 배열을 원하는 [SKSpriteNode?]것과는 다릅니다 [SKSpriteNode]?.)

Swift는 초기화되지 않은 참조의 내용에 대한 가정이 C (및 일부 다른 언어)의 프로그램이 버그가 될 수있는 방법 중 하나이기 때문에 변수를 초기화하도록 요구하는 설계 상 매우 명시 적입니다. 따라서 [SKSpriteNode?]64를 포함 하는 배열 을 명시 적으로 요청해야합니다 nil.

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

이것은 실제로 [SKSpriteNode?]?, 선택적 스프라이트의 선택적 배열을 반환합니다 . (조금 이상 init(count:,repeatedValue:)합니다. nil을 반환 할 수 없어야하기 때문입니다.) 배열을 사용하려면 배열을 풀어야합니다. 이를 수행하는 몇 가지 방법이 있지만이 경우 선택적 바인딩 구문을 선호합니다.

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


답변

지금 할 수있는 최선의 방법은 초기 횟수가 nil을 반복하는 배열을 만드는 것입니다.

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

그런 다음 원하는 값을 입력 할 수 있습니다.


에서 스위프트 3.0 :

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


답변

이 질문은 이미 답변되었지만 Swift 4 당시의 추가 정보는

성능의 경우 배열을 동적으로 생성하는 경우 (예 : Array.append().

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

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

추가 할 요소의 최소량을 알고 있지만 최대량은 알고 있지 않다면 array.reserveCapacity(minimumCapacity: 64).


답변

빈 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) 메모리 레이아웃이 정의되지 않았습니다. (적어도 나에게 알려지지 않음)


답변

스위프트 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: 무시됩니다 빈 선택적 항목, 즉 건너 뜁니다.


답변

원하는 것이 고정 크기 배열이고 nil값으로 초기화하는 경우을 사용하고 UnsafeMutableBufferPointer64 노드에 메모리를 할당 한 다음 포인터 유형 인스턴스를 첨자하여 메모리에서 읽고 쓸 수 있습니다. 또한 메모리를 재 할당해야하는지 여부를 확인하지 않아도되는 이점이 있습니다 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대신 a로 변경할 수 있지만 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!)
}

준수를 구현하는 더 많은 프로토콜은 배열 문서를 참조하십시오 ( 관계로 스크롤 ).