티스토리 뷰

Swift

Swift ) The Swift Array Design

Zedd0202 2018. 9. 27. 17:17
반응형

안녕하세요 :) Zedd입니다.

OptimizationTips에서 궁금했던.. ContiguousArray......

사실 처음들어봐서 굉장히 궁금해지게 만들었음ㅡㅡ

그래서 공부하려다가..그 전에 Swift github에 "The Swift Array Design"이라는 게 있어서요..

암튼 이거 보고 ContiguousArray를 공부하도록 하겠습니다!!


The Swift Array Design


Goals(목표)

- 클래스가 아닌 element(non-class element) 타입의 subscript get / set에 대해, C배열과 동일한 성능을 내는 것이 가장 중요한 성능 목표입니다.

- Cocoa에서  NSArray를 받아서(receive) Array<AnyObject>로 표현하고, 메모리 할당 없이, 바로 Cocoa에서 NSArray로 O(1)에 전달 할 수 있어야 합니다.
(Cocoa NSArray -> Array<AnyObject> 이게 별도의 메모리 할당없이 상수시간(O(1))에 일어나길 원한다는 것)

- Array는 Stack으로 사용 할 수 있어야 하므로, amortized O(1) append / PopBack을 원합니다. 목표 1번과 같이, 실제 저장된 요소의 수를 초과 할 수 있는 예약된 tail memory capacity를 갖는 std::vector와 같은 레이아웃을 의미합니다. 



목표 1번과 2를 달성하기 위해, element타입에 대한 정적 지식??..(static knowledge)을 사용합니다. (아 컴파일타임에 알 수 있다는 그런걸 의미하는건가?)

element타입이 클래스가 아닌 것으로 static으로 알려진 경우, NSArray를 wrapping할 가능성을 설명하는 코드 및 검사가 제거됩니다.

Swift value type의 배열의 항상 ContiguousArray와 동일하게, 가능한 한 가장 효율적인 표현(representation)을 사용합니다. 

오 ContiguousArray이야기 나와땀

그리고 제가 궁금했던 Swift에서 value type과 reference type의 차이점이, value Type은 NSArray 내부에 포함이 안된다 < 이걸 알 수도 있을지도!?!?!?!?!?!?!?!

Components


Swift는 일반적으로, 3가지 타입의 배열을 제공하며, 모두 amortized O(1) growth를 가집니다.

이 문서에서 ArrayType에 대한 설명은 세가지 타입 모두에 적용됩니다. 


 ContiguousArray<Element>는 세가지 타입들 중에서 가장 빠르고 간단합니다.

"C 배열"성능이 필요할 때 사용합니다.

ContiguousArray의 element는 항상 메모리에 연속적으로 저장됩니다.




위 그림처럼 "연속적으로" 저장되는것이 ContiguousArray라고 합니다.


 Array<Element>는 ContiguousArray<Element>와 비슷하지만, Cocoa에서 효율적인 conversions을 위해 최적화되었습니다. Element는 class타입이 될 수 있지만, Array<Element>는 Swift의 ContiguousArray보다 오히려 임의의 NSArray의 저장소에 의해 백업(potentially non-contiguous, contiguous가 아니랍니다) 될 수 있습니다. 

Array<Element>는 관련된 class타입의 배열 사이에서, 업 / 다운 캐스트를 지원합니다. 

Element가 클래스가 아닌 타입으로 알려진 경우(== 값타입인 경우) Array<Element>의 성능은 ContiguousArray<Element>의 성능과 동일합니다.


엄청 중요한 말들이 지나간 느낌인데요.. 

"Swift에서 value type과 reference type의 차이점이, value Type은 NSArray 내부에 포함이 안된다 "

이게...이게 그니까 지금은 만약에 Element가 클래스타입이면, 즉 reference type이면 임의의 NSArray저장소에 의해 백업될 수 있다. 그니까 NSArray 내부에 포함될 수 있다. 근데 value type이면 안그런다????? 그뜻인거 같은데여?!?!?!?!?!?!?!?

마지막줄 보면 value type이면 "Array<Element>의 성능은 ContiguousArray<Element>의 성능과 동일합니다." < 이라고 했으니까 value type이면 연속적으로 저장된다는 것인가??!?!?!?????????????? 그래서 "Swift에서 value type과 reference type의 차이점이, value Type은 NSArray 내부에 포함이 안된다 "

이렇게 말한거아ㅣ가ㅣㅏ아아
오..........이렇게 밝혀지다니...아직 확실하진 않지만...이러한 이유때문에 문서에 NSArray가 언급된거 아닐ㄱ까 생각이 드네요.

!!!!!!!!!!!!!!!!!!!!!!!! 좋다 기분 알아서


● ArraySlice<Element> 는 Array<Element> 또는 ContiguousArray<Element>의 subrange입니다. ArraySlice<Element>는 slice notation(slice 표기법)을 사용한 결과입니다. (e.g. 이름이 a인 Swift배열에서 a[7...21] )

slice에는 항상 contiguous storage 및 "C 배열" 성능을 가집니다. 

contiguous storage를 제공하지 않는 NSArray에 의해 백업되는 소스가 Array<Element>가 아닌이상(위에서 말한 value type이 아닌 reference type == 클래스타입을 담았을 때, NSArray에 백업될 수 있다고 그랬잖아요?) ArrayType을 slice하는 것은 O(1)입니다.


ArraySlice는 일시적인(transient) 계산에는 권장되지만, 장기 저장(long-term storage)에는 권장되지 않습니다.

일부 공유된 백업 버퍼의 sub-range를 참조하기 때문에 ArraySlice는 ArraySlice 외부 요소의 lifetime을 인위적으로 연장 할 수 있습니다.




Mutation Semantics

ArrayTypes에는 COW(copy-on-write)를 통한 full value semantic을 가집니다.

var a = [1, 2, 3]

let b = a

a[1] = 42

print(b[1]) // prints "2"



Bridging Rules and Terminology for all Types

- 모든 클래스 타입 또는 @objc existential (뭐라 번역해야지..? such as AnyObject라고 합니다.)은 Objective-C에 브릿징되며, identity transformation을 통해 Swift로 다시 브릿징됩니다. 
- 동일하게 브릿징되지않는 타입 T는 BridgedToObjectiveC를 준수할 수 있습니다. BridgedToObjectiveC는 Objective-C와의 변환(conversions)을 명시합니다.

protocol _BridgedToObjectiveC {

    typealias _ObjectiveCType: AnyObject

    func _bridgeToObjectiveC() -> _ObjectiveCType

    class func _forceBridgeFromObjectiveC(_: _ObjectiveCType) -> Self

}


Note : 클래스와 @objc existentials은 _BridgedToObjectiveC를 준수하지 않아야 합니다. 제한(restriction)은 컴파일타임에 적용 할 수 없습니다. 


일부 Generic type(ArrayType<T>)은 element타입이 브릿징 될 경우에만 Objective-Cㅇㅔ 브릿징됩니다. 이러한 타입은 _ConditionallyBridgedToObjectiveC를 준수합니다.


protocol _ConditionallyBridgedToObjectiveC : _BridgedToObjectiveC {

    class func _isBridgedToObjectiveC() -> Bool

    class func _conditionallyBridgeFromObjectiveC(_: _ObjectiveCType) -> Self?

}


T._isBridgedToObjectiveC()가 false일 때, _ConditionallyBridgedToObjectiveC를 준수하는 유형 T에서 브릿징하는것은 런타임에 진단 될 수 있는 사용자 프로그래밍 에러입니다.

_conditionallyBridgeFromObjectiveC를 사용하여 다시 브릿징 하려고 시도하고, 전체 객체를 브릿징 할 수 없는 경우, nil을 반환합니다.


Implementation Note : 이러한 detection을 컴파일타임으로 옮기는 다양한 방법이 있습니다.

있다는데, 이건 알아서 읽어보시길.


이 이후에 정말 알아들을 수 없는 내용들이 나오는데, 이것도 알아서 읽으시길 바랍니다.

근데 이 이후에 나오는 내용들을 요약??한 것이 Array문서에 언급이 되어있는 것 같아서 이거는 공부해보도록 하겠습니닷 XD


Bridging Between Array and NSArray


Array가 아닌 NSArray 인스턴스의 데이터가 필요한 API에 접근해야하는 경우, type-cast연산자인 as를 사용하여 인스턴스를 브릿징하세요. (연결하라는 뜻임) 브릿징이 가능하려면, 배열의 element타입이 클래스, @objc프로토콜(Objective-C에서 가져온 프로토콜 또는 @objc 속성으로 표시된 프로토콜) 또는 Foundation타입에 브릿징되는 타입이어야 합니다.

다음 예제에서는 Array인스턴스를 NSArray에 브릿징하여 write(to:atomically:)메소드를 사용하는 방법을 보여줍니다. 



let colors = ["periwinkle", "rose", "moss"]

let moreColors: [String?] = ["ochre", "pine"]


let url = NSURL(fileURLWithPath: "names.plist")

(colors as NSArray).write(to: url, atomically: true)

// true


(moreColors as NSArray).write(to: url, atomically: true)

// error: cannot convert value of type '[String?]' to type 'NSArray'


colors배열의 String element가 NSString으로 브릿징 될 수 있기 때문에, colors배열을 NSArray에 브릿징 할 수 있습니다.

하지만, 컴파일러는 Element타입이 Foundation타입에 브릿징 되지 않는 Optional<String> = String? 이므로 moreColors배열이 브릿징되지 못합니다.


배열 element가 이미 클래스 또는 @objc프로토콜의 인스턴스인경우, Array에서 NSArray로의 브릿징은 O(1)시간과 O(1)공간을 사용합니다. 그렇지 않으면 == 배열 element가 클래스 || @objc프로토콜의 인스턴스가 아닌경우 O(n) 시간/공간이 필요합니다.


destination 배열(대상 배열...?)의 element타입이 클래스 또는 @objc프로토콜 인경우, NSArray에서 Array로의 브릿징은,

먼저 배열의 copy(with:) (- copyWithZone: in Objective-C) 메소드를 호출 하여 immutable(변경 불가능한) 복사본을 가져온 다음, O(1)시간이 걸리는 Swift bookkeeping작업(bookkeeping이라는 뜻이 사업의 재무(financial)기록을 하는 그런 활동을 의마한다는데...)을 수행합니다.


이미 immutable인 NSArray인스턴스의 경우, copy(with:)는 대게 O(1)시간에 동일한 배열을 반환합니다.

copy(with:)가 동일한 배열을 반환하면, NSArray및 Array 인스턴스는 Array의 두 인스턴스가 저장소를 공유 할 때 사용되는 것과 동일한 copy-on-write optimization을 사용하여 저장소를 공유합니다.


destination배열의 element타입이 Foundation타입에 연결되는 non-class type인 경우, NSArray에서 Array로의 브릿징은 element를 O(n)시간에 contiguous storage에 복사본을 브릿징합니다.


예를들어, NSArray에서 Array<Int>로의 브릿징은 이러한 copy를 수행합니다. Array인스턴스의 element에 접근 할 때, 추가 브릿징이 필요하지 않습니다.


ContiguousArray 및 ArraySlice타입은 브릿징되지 않습니다. ContiguousArray 및 ArraySlice는 항상 contiguous(연속적인) 메모리 블록을 스토리지로 사용합니다.


gㅎㅎㅎㅎ이렇게...알아보았는데요, 

In Swift, types can be divided into two different categories: value types (structs, enums, tuples) and reference types (classes). A key distinction is that value types cannot be included inside an NSArray."

이렇게 inside라고 말한 이유가 있었군요...ㅎㅎ헿ㅎ핳ㅎㅎㅎ그래도 왜 문서에서 이렇게 말했는지 이유를 알아서 기분 좋네요!!

도움이 되었길 바랍니다 :)



반응형