- Fundamentals
- Collection프로토콜을 통해서 많은 컨테이너가 공통적인 기능을 공유한다.
- extension으로 Collection에 새로운 기능을 추가하기도 쉽다.
- 콜렉션은 또 다양한 하위 범주를 가진다
- BidirectionalCollection: 앞으로도 갈 수 있는 Collection
- RandomAccessCollection: 임의의 원소에 상수 시간에 접근할 수 있는 Collection -> 강제하지는 않지만 그렇게 구현해야 한다
- MutableCollection
- RangeReplaceableCollection
- Indices and Slices
- Indices: 모든 Collection은 각자의 index 타입을 가진다
- 반드시 Comparable해야 한다.
- 구체 타입을 모르는 채로 써야 한다.
- Slice: Collection의 일부를 나타내는 타입
- Collection과 동일한 버퍼와 인덱스를 공유한다.
- 각 Collection은 각자의 Slice타입을 가진다.
- Slice는 각 Collection 버퍼의 참조처럼 동작한다.
- Slice가 있는 상태에서 원본 참조를 날려도 버퍼는 살아있다.
- 그럼 removeAll하면 어떻게 될까? → 원본 참조와 연관성이 사라진다.
- Eager vs Lazy
- Eager: 호출 즉시 작업을 모두 실행한다.
- Lazy: 값을 참조한 시점에서야 연산을 실행한다. (Be Lazy, Exactly Once)
- Lazy를 언제 쓸까?
- 체이닝 연산
- 결과의 일부만 필요할 때
- 사이드 이펙트가 없을 때
- API 경계에서는 쓰지 말 것
- MutableCollection: 길이의 변화 없이, 내용물을 변화시킬 수 있는 콜렉션
- RangeReplaceableCollection: 특정 범위를 다른 콜렉션으로 대체할 수 있는 콜렉션
- pitfall
- Index는 collection이 변화하면 invalidate된다. -> collection이 바뀌면 재계산 해야된다.
- slice는 원본 콜렉션이 바뀌더라도 오리지널을 가지고 있다.
- 따라서 Slice나 index는 주의해서 다뤄야 한다.
- Collection은 thread-safe하지 않다.
- 상호 배제를 지키고, TSAN으로 측정하라
- Immutable을 유지하고, slice나 lazy를 통해서 mutation을 emulating하라 -> 컴파일러가 최적화를 도와준다.
- capacity를 미리 정할 수 있으면 정해라
- Foundation이 제공하는 Collection
- Reference 타입
- CoW 지원할 필요가 없다. 그저 또 다른 포인터일 뿐
- Swift와 Foundation은 어떻게 연결되는가 지원하는가? -> Bridging
- 양방향 모두 가능
- 비용이 작지만, 무비용은 아니다.
- Bridging 종류
- Eager: Element가 Bridging이 필요한 경우
- Lazy: Element가 Bridging이 필요없는 경우(Objective-C 타입)
- Bridging으로 일어날 수 있는 문제를 확인하려면?
- Instrument로 성능 체크
- 루프 안에서 브릿징이 일어나지 않는지 체크해보자.
let story = NSString("...") // 긴 텍스트
let text = NSMutableAttributedString(string: story)
// 브릿징 구간 - 개선 전
// let range = text.string.range(of: "Brown")!
// let nsrange = NSRange(range, in: text.string)
//
// 브릿징 구간 - 개선 후
// let string = text.string // 브릿징을 한번만 한다.
// let range = string.range(of: "Brown")
// let nsrange = NSRange(range, in:string)
//
// 브릿징 구간 - 더 개선
let text = NSMutableAttributedString(string: story)
let string = text.string as NSString // 브릿징 안함
let nsrange = string.range(of: "Brown") // 여기서도 작지만 브릿징이 일어나고 있다(Swift 타입을 가지고 Objective-C API 호출)
text.addAttribute(.foregroundColor, value: NSColor.brown, range: nsrange)
- 언제 Foundation의 콜렉션을 쓸까?
- 참조 형식이 필요할 때
- 알려진 프록시들(NSAttributedString.string, Core Data Managed Object 등)을 사용할 때
- 측정해봤더니 브릿징 코스트가 많이 들 때