@ WWDC 18
// Declaraton of Collection
protocol Collection: Sequence {
associatedtype Element
associatedtype Index: Comparable
subscrippt(position: Index) -> Element { get }
var startIndex: Index { get }
var endIndex: Index { get }
func index(after i: Index) -> Index
}
extension Collection {
func everyOther(_ body: (Element) -> Void) {
let start = self.startIndex
let end = self.endIndex
var iter = start
while iter != end {
body(self[iter])
let next = index(after: iter)
if next == end { break }
iter = index(after: next)
}
}
}
(1...10).everyOther { print($0) }
- Each collection defines its own index
- Must be
Comparable
- Think of these as opaque
extension Collection {
var secnod: Element? {
guard self.startIndex != self.endIndex else { return nil }
let index = self.index(after: self.startIndex)
guard index != self.endIndex else { return nil }
return self[index]
}
}
let array = [1, 2, 3, 4, 5]
let subarray = array.dropFirst()
let secondIndex = array.index(after: array.startIndex)
print(secondIndex == subarray.startIndex)
var second: Element? {
return self.dropFirst().first
}
-
Produce
Collection-
like peers of original collection -
Array -> ArraySlice
-
String -> Substring
-
Set -> Slice
-
Data -> Data
-
Range -> Range
extension Array {
var firstHalf: ArraySliice<Element> {
return self.droppLast(self.count / 2)
}
}
var array = [1, 2, 3, 4, 5, 6, 7, 8]
var firstHalf = array.firstHalf
array = []
print(firstHalf.first!) // 1
let copy = Array(firstHalf)
firstHalf = []
print(copy.first!) // 1
์น ๋ค ๊ณ์ฐํจ
let items = (1...4000).map { $0 * 2 }.filter { $0 < 10 }
ํ์ํ ๊ฑฐ์ ์์ฃผ๋ก
let items = (1...4000).lazy.map { $0 * 2 }.filter { $0 < 10 }
items.first
- Chained computation
- Only need part of a result (์ ์ฒด ๊ฒฐ๊ณผ ํ์ ์์ ๋!)
- No side effects
- Avoid API boundaries
- collection์ mutating ์ํค๊ณ ์์ง ์๋ ์ง ์ฒดํฌํด๋ด๋ผ
- ์ฌ๋ฌ thread์์ collections์ ์ ๊ทผํ๊ณ ์์ง๋ ์์๊ฐ?
- Use caution when keeping indices/slices
- Mutation invalidates
- Calculate only as needed ๋ฑ ํ์ํ ๋๋ง ๊ณ์ฐํด์ ์ ๊ทผํด๋ผ
- Our collections optimized for single-threaded access
- This is a Good Thing
- Undefined behavior without mutual exclusion
// Thread-Unsafe Ppractices
var sleepingBears = [String]()
let queue = DispatchQueue.global()
queue.async { sleepingBears.append("Grandpa") }
queue.async { sleepingBears.append("Cub") }
// Avoid concurrent mutation
let queue = DispatchQueue(label: "Bear-Cave")
- Prefer state accessible from a single thread
- When this is not possible:
- Ensure mutual exclusion
- Use TSAN
- Easier to reason about data that can't change
- Less surface area for bugs
- Emulate mutation with slices and lazy
- The compiler will help you
- Use capacity hints if possible
Array.reserveCapacity(_:)
Set(minimumCapacity:)
Dictionary(minimumCapacity:)
- Converts between runtime types
- Bidirectional
- Bridging of collections
- Is necessary
- Can be cheap, but is never free
- Eager when element types are bridged
- Otherwise lazy
- Bridged on first use
- Measure your performance with Instruments
- Especially inside loops at language boundaries
- Look for hotspots like:
_unconditionallyBridgeFromObjectiveC
bridgeEverything
let story = NSString(string: "๋ญ๋ผ๋ญ๋ผ ๋ญ๋ผ๋ญ๋ผ๋ญ๋ผ๋ฌ๋ผใ
๋ผ๋ผ๋ญ๋ผ๋ผ๋ผใ
")
let text = NSMutableAttributedString(string: story)
// ์ฒซ ๋ฒ์งธ ๋ฒ์ (Bridging overhead๊ฐ ํผ)
let range = text.string.range(of: "Brown")!
let nsrange = NSRange(range, in: text.string)
// ๋ ๋ฒ์งธ ๋ฒ์
let string = text.string // ์ฌ๊ธฐ์ ์ค๋ฒํค๋ ํ ๋ฒ ๋ฐ์
let range = text.string.range(of: "Brown")!
let nsrange = NSRange(range, in: text.string)
// ์ธ ๋ฒ์งธ ๋ฒ์
let string = text.string as NSString
let nsrange = string.range(of: "Brown")
text.addAttribute(.forgroundColor, value: NSColor.brown, range: nsrange)
- You need reference semantics
- You are working with known proxies
NSAttributedString.string
- Core Data Managed Objects
- You've measured and identified bridging costs
- Explore your existing collections
- Measure your code
- Audit your mutable state
- Gain mastery in Playgrounds