Swift 5.4 Released!
안녕하세요 :) Zedd입니다.
Xcode 12.5 Beta Release Notes 📝의 Swift > New feature부분에서 다루긴 했는데,
안본것도 있고 그러니 호다닥 공부해보겠습니다.
Swift 5.4는 다음과 같은 새로운 언어 feature가 포함됩니다.
- functions, subscripts, initializers에서 multiple variadic parameters 지원.
- implicit member syntax확장
- Result builders
- overloading을 지원하는 Local functions(지역 함수)
- local variables(지역 변수)에 대한 Property wrappers
# [SE-0284] functions, subscripts, initializers에서 multiple variadic parameters 지원.
func foo(_ a: Int..., b: Double...) { }
struct Bar {
subscript(a: Int..., b b: Int...) -> [Int] { a + b }
init(a: String..., b: Float...) { }
}
원래
single variadic parameter만 가능했고, 한개 이상하려고 하면 위와같은 오류가 났는데
Swift 5.4에서는 레이블만 달려있으면
1개 이상의 variadic parameter가 가능하다.
# [SE-0287] implicit member syntax확장
Swift 5.4에서는 다음 코드가 유효한것으로 판단.
let milky: UIColor = .white.withAlphaComponent(0.5)
let milky2: UIColor = .init(named: "white")!.withAlphaComponent(0.5)
let milkyChance: UIColor? = .init(named: "white")?.withAlphaComponent(0.5)
SwiftUI의 경우
Text("Hello, world!")
.foregroundColor(.red.opacity(0.5))
이런게 가능해지는 것.
원래
Text("Hello, world!")
.foregroundColor(Color.red.opacity(0.5))
이렇게 Color라고 꼭 써줘야됐음.
+ Swift 5.4이전에서도 당연히
Text("Hello, world!")
.foregroundColor(.red)
이거는 된다. 체이닝이 안됐던 것 뿐.
# [SE-0289] Result builder
이건 잘 이해가 안가니 나중에 따로 글을...
# [SR-10069] overloading을 지원하는 Local functions(지역 함수)
💡 overloading(오버로딩) : 같은 이름의 메서드 여러개를 가지면서 매개변수의 타입과 개수가 다르도록 하는 기술
ƒ라는 함수 안에서 nested라는 이름을 가진 지역 함수 2개를 작성.
이름은 같지만 파라미터 타입이 다르므로 오버로딩이 사용(?)되었다고 할 수 있다.
Swift 5.4이전 - 컴파일에러
Swift 5.4이후 - OK!
# local variables(지역 변수)에 대한 Property wrappers
Property wrapper에 대한 내용은 Property Wrapper 글 참고.
(위 글에 대한 예제를 그대로 사용하겠음)
@propertyWrapper
struct Uppercase {
private var value: String = ""
var wrappedValue: String {
get { self.value }
set { self.value = newValue.uppercased() }
}
init(wrappedValue initialValue: String) {
self.wrappedValue = initialValue
}
}
이런 로직들을 Property wrapper로 만들고,
struct Address {
@Uppercase var town: String
}
이런식으로 사용이 가능했음.
하지만
func f() {
@Uppercase let town: String
}
이렇게 지역변수로 Property wrapper를 만들면
local variables은 아직 Property wrapper를 지원안한다고 컴파일에러.
Swift 5.4부터는 이것도 가능
# Runtime Performance and Code Size Improvements
1. Swift 5.4에서는 previous lookup 결과를 캐싱하기 위해 더 빠른 Hash Table구현
→ 프로토콜 적합성 검사가 훨씬 빨라짐
→ 공통 런타임 as? 과 as! casting operations 속도가 높아짐
2. 연속적인(consecutive) Array 수정은 중복된 고유성 검사를 방지한다.
func foo(_ a: inout [Int]) {
// copy-on-write (CoW) 체크를 여기서 반드시 한다.
a[0] = 1
// 컴파일러는 여기서 중복 COW검사를 생성하지 않음.
a[1] = 2
}
3. 다양한 성능 향상
- 문자열 보간(String interpolations)은 보다 적극적으로 일정하게 접힌다..? (more aggressively constant-folded)
- inout 함수 argument 및 루프 내에서 retain/release 호출 감소
- 표준 라이브러리의 일반 메타 데이터는 이제 컴파일 타임에 specialize되어 dirty memory사용량을 줄이고 성능을 향상시킨다.
# Build Performance
1. Swift 컴파일러는 파일간의 종속성을 추적하는데 훨씬 더 뛰어나졌음
→ 증분 빌드 중에 여러 종류의 변경사항에 대해 컴파일 된 파일 수가 크게 감소.
2. 이제 멤버 변수 및 구조체, 열거형, 클래스 및 프로토콜의 함수에 대한 종속성은 Swift 컴파일러에 의해 개별적으로 추적됨
→ 이러한 엔티티 변경 후 rebuilds 속도가 빨라지고 축소된다? (shrinks)
3. 증분 빌드는 더 많은 경우에 deterministic products를 생산한다..(Incremental builds produce deterministic products in many more cases. 이건 뭔소린지 모르겠음)
# Code Completion
- Code Completion의 성능은 큰 함수 본문(large function bodies)에서 훨씬 빨라졌음.
swift-package-manager repository 한 예제에서 self. 에 대한 Code Completion은 Swift 5.3에 비해 4배 빨라짐(20ms → 4ms)
- Code Completion은 오류가 포함된 expressions과 추가 context가 없는 모호한(ambiguous) 식에서도 더욱 안정적이 됨.
func test(a: Int, b: String) -> Int { ... }
func test(a: Int, b: Int) -> String { ... }
func test(a: (Int, Int) -> Int) -> Int { ... }
위 코드의 경우
test().prefix(3). 후 code completion은 String멤버를 제안.
test(a: 2). 후 code completion은 Int 및 String 멤버를 제안.
test { $0. } 후 $0. code completion은 블록에서 Int멤버를 제안.
# Type Checker
1. Swift 5.4는 a + b + (2 *c)와 같은 "연결된" expressions에 대한 타입 검사 성능을 향상.
ex.
struct S { var s: String? }
func test(_ a: [S]) {
_ = a.reduce("") {
($0 + "," + ($1.s ?? "")) + ($1.s ?? "") + ($1.s ?? "")
}
}
위 코드의 경우 원래는 시간 초과가 떴지만,
(이렇게)
Swift 5.4컴파일러에서는 100ms이내에 완료된다고 함.
2. Type Checker는 다른 리터럴 표현식을 포함하는 중첩된 배열 리터럴에 대한 성능을 개선.
ex.
enum E {
case first
case second
case third
}
let dictionary = [
.first: [0, 1, 2, 3, 4, 5, 6, 7],
.second: [8, 9, 10, 11, 12, 13, 14, 15],
.third: [16, 17, 18, 19, 20, 21, 22, 23],
]
위 코드의 경우에는 사실 컴파일에러가 떠야함. 왜냐? enum E에 대한 추론을 할 수 없기 때문.
하지만 그런 에러는 안뜨고 “too complex to solve in reasonable time”라는 메세지가 뜬다.
(이렇게)
Swift 5.4에서는 정확한 오류메세지와 함께 코드를 유효하지 않은 것으로 진단함.
error: reference to member 'first' cannot be resolved without a contextual type
.first : [ 0, 1, 2, 3, 4, 5, 6, 7],
^
error: reference to member 'second' cannot be resolved without a contextual type
.second : [ 8, 9, 10, 11, 12, 13, 14, 15],
^
error: reference to member 'third' cannot be resolved without a contextual type
.third : [16, 17, 18, 19, 20, 21, 22, 23],
^
3. Type checker가 잘못된 statements(e.g. invalid return statement), 잘못된 선언 참조 및 패턴 일치 오류를 포함하여 결과 빌더에 대한 진단 개선
import SwiftUI
struct ContentView: View {
@State private var condition = false
var body: some View {
Group {
if condition {
Text("Hello, World!")
.frame(width: 300)
} else {
return Text("Hello, World!")
}
}
}
}
위 코드의 경우
이런 에러가 떴지만, Swift 5.4 컴파일러에서는
error: cannot use explicit 'return' statement in the body of result builder 'SceneBuilder'
return Text("Hello, World!")
정확한 오류를 보고하게 됨.
# Debugging
Apple 플랫폼에서 Swift 코드를 디버깅 할 때 resilient type(탄력적인 타입. URL, URLComponents, Notification, IndexPath, Decimal, Data, Date, Global, Measurement, UUID를 포함한 Foundation value types)을 가진 변수가 Xcode variable view 와 frame variable에 표시됨.
참고