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


101

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

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

var sprites = SKSpriteNode()[64];

또는

var sprites4 : SKSpriteNode[64];

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

답변:


147

고정 길이 배열은 아직 지원되지 않습니다. 이것은 실제로 무엇을 의미합니까? 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
}

고마워요, 그거 해봤는데 "?"는 잊어 버렸어요. 그러나 여전히 값을 변경할 수 없습니까? 나는 둘 다 시도했다 : 1) sprites [0] = spritePawn 및 2) sprites.insert (spritePawn, atIndex : 0).
Henri Lapierre

1
놀라다! sprites추론 된 유형을 보려면 편집기 / 플레이 그라운드에서 Cmd- 클릭 합니다 SKSpriteNode?[]?. 실제로 는 선택적 스프라이트의 선택적 배열입니다. 선택 사항을 아래 첨자 할 수 없으므로 포장을 풀어야합니다 ... 수정 된 답변을 참조하십시오.
rickster

참으로 이상합니다. 당신이 언급했듯이, 우리가 명시 적으로 그것을? []?가 아니라? []로 정의했기 때문에, 나는 배열이 선택적이어야한다고 생각하지 않습니다. 필요할 때마다 포장을 풀어야하는 성가신 일입니다. 어쨌든 이것은 작동하는 것 같습니다 : var sprites = SKSpriteNode? [] (count : 64, repeatValue : nil); if var unwrappedSprite = sprites {unwrappedSprite [0] = spritePawn; }
Henri Lapierre

Swift 3 및 4의 구문이 변경되었습니다. 아래 다른 답변을 참조하십시오
Crashalot

61

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

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

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


에서 스위프트 3.0 :

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

5
고정 크기의 배열 을 선언 하는 방법이 있습니까?
アレックス

2
@AlexanderSupertramp 더 배열하는 크기 선언 할 방법이 없다
drewag

1
@ ア レ ッ ク ス 배열의 고정 크기를 선언하는 방법은 없지만 고정 크기를 적용하는 배열을 래핑하는 고유 한 구조체를 확실히 만들 수 있습니다.
drewag

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 질문을 해결하고 또한 배열이 동일한 인스턴스 객체로 채워져 있다는 것을 알면 그것을 처리해야 할 것입니다.
Carlos.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값으로 초기화하는 경우을 사용하고 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!)
}

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


-3

당신이 할 수있는 한 가지는 사전을 만드는 것입니다. 64 요소를 찾는 것을 고려하면 약간 엉성 할 수 있지만 작업이 완료됩니다. "선호하는 방법"인지 확실하지 않지만 구조체 배열을 사용하여 저에게 효과적이었습니다.

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

2
어레이보다 어떻게 낫습니까? 나에게 그것은 문제를 해결하지 못하는 해킹이다. 당신은 tasks[65] = foo이 경우와 질문에서 배열의 경우 모두에서 아주 잘 할 수있다 .
LaX
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.