연산자
사용자 정의 연산자
토큰으로 사용되는 =, ->, //, /*, */
등과 전위 연산자 <, &, ?
, 중위 연산자 ?
, 후위 연산자 >, !, ?
등은 Swift에서 예약한 상태이므로 재정의할 수 없으며 사용자 정의 연산자로 사용될 수 앖다.
전위 연산자 정의와 구현
prefix operator **
prefix func **(value: Int) -> Int {
return value * value
}
후위 연산자 정의와 구현
postfix operator **
postfix func **(value: Int) -> Int {
return value * value
}
중위 연산자 정의와 구현
중위 연산자는 우선순위 그룹precedenceGroup을 명시해줄 수 있음
precedenceGroup 우선순위_그룹_이름 {
higherThan: 더_낮은_우선순위_그룹_이름
lowerThan: 더_높은_우선순위_그룹_이름
associativity: 결합방향(left / right / none)
assignment: 할당방향_사용(true / false)
}
Swift에 정의된 우선순위 그룹이 있음.
infix operator **: MultiplicationPrecedence
func **(lhs: String, rhs: String) -> Bool {
return lhs.contains(rhs)
}
함수
종료되지 않는 함수
비반환 함수(메소드)는 반환 타입을 Never
로 명시해준다.
func crash() -> Never {
fatalError("CRASH!")
}
crash() // 프로세스 종료 후 오류 보고
옵셔널
- 옵셔널은 열거형으로 구현되어 있음! 그러므로
switch
문으로some
과none
인 케이스에 접근할 수 있으나 일반적을 옵셔널 바인딩 등의 방법을 활용한다. - 함수의 매개 변수 타입이 옵셔널이면, 매개 변수 기본값이
nil
이 아니어도 함수 호출 시 그 부분에 아무런 값을 주지 않아도nil
이 들어올 것임을 알아야 한다.
구조체와 클래스
구조체를 사용해야 하나? 클래스를 사용해야 하나?
다음의 조건 중 하나 이상에 해당된다면 구조체를 사용하는 것을 권장한다.
- 연관된 간단한 값의 집합을 캡슐화하는 것만이 목적일 때
- 캡슐화한 값을 참조하는 것보다 복사하는 것이 합당할 때
- 구조체에 저장된 프로퍼티가 값 타입이며 참조하는 것보다 복사하는 것이 합당할 때
- 다른 타입으로부터 상속받거나 자신을 상속할 필요가 없을 때
프로퍼티와 메소드
지연 저장 프로퍼티
지연 저장 프로퍼티lazy stored property는 호출이 있어야 값을 초기화하며, 주로 복잡한 클래스나 구조체를 구현할 때 많이 사용된다.
- 다중 스레드 환경에서 지연 저장 프로퍼티에 동시다발적으로 접근할 때 한 번만 초기화된다는 보장이 없다.
키 경로
- 키 경로keyPath를 활용하여 어떤 프로퍼티의 위치만 참조할 수 있도록 할 수 있다. 키 경로를 사용하여 간접적으로 특정 타입의 어떤 프로퍼티 값을 가리켜야 할지 미리 지정해두고 사용할 수 있다.
- 각 인스턴스의
KeyPath
서브스크립트 메소드에 키 경로를 전달하여 프로퍼티에 접근할 수 있다. - 키 경로를 잘 활용하면 타입 간의 의존성을 낮추는 데 많은 도움을 준다.
- 키 경로는 타입 외부로 공개된 인스턴스 프로퍼티에 한하여 표현할 수 있다.
class Person {
var name: String
init(name: String) {
self.name = name
}
}
let person = Person(name: "presto")
let keyPath = \Person.name
person[keyPath: keyPath] // Presto
self 프로퍼티
인스턴스 메소드에서는
self
가 인스턴스를 가리키지만, 타입 메소드에서는self
가 타입 그 자체를 가리킨다.self
프로퍼티를 활용하여 값 타입 인스턴스 자체의 값을 치환할 수 있다.- 클래스의 인스턴스는 참조 타입이므로
self
프로퍼티에 다른 참조를 할당할 수 없다.
- 클래스의 인스턴스는 참조 타입이므로
struct Level {
var level: Int = 0
mutating func levelUp() {
level += 1
}
mutating func reset() {
self = Level()
}
}
타입 메소드
static
키워드를 사용하여 정의할 수 있으며, 상속 후 메소드 정의를 가능하게 하려면 class
키워드를 사용하여 정의한다.
접근 제어
설정자만 더 낮은 접근 수준을 갖도록 제한할 수 있다.
public struct SomeType {
// 저장 프로퍼티의 접근 수준 다르게 하기. 설정자는 비공개 접근수준, 접근자는 공개 접근수준.
public private(set) var property: Int = 0
// 서브스크립트의 접근 수준 다르게 하기. 설정자는 내부 접근수준, 접근자는 공개 접근수준.
public internal(set) subscript(some: Int) -> Int {
get { return property }
set { property += some }
}
// 저장 프로퍼티도 같은 방법으로 구현할 수 있다.
}
클로저
클로저는 참조 타입
자동 클로저
- 자동 클로저는 전달 인자를 갖지 않는다.
- 자동 클로저는 호출되었을 때 자신이 감싸고 있는 코드의 결과값을 반환한다.
- 자동 클로저는 클로저가 호출되기 전까지 클로저 내부의 코드가 동작하지 않으므로 연산을 지연시킬 수 있다.
- 자동 클로저 속성을 부여한 매개변수는 클로저 대신에 실행 결과를 전달인자로 받게 되며, 이것이 자동 클로저 매개변수에 전달되면 매개변수가 없는 결과 값의 타입을 반환하는 클로저로 변환해준다.
var strings = ["a", "b", "c"]
func first(_ function: @autoclosure () -> String) {
function()
}
first(strings.removeFirst())
// strigns.removeFirst()의 결과인 a가 전달인자로 들어가며, String 값을 반환하는 매개변수가 없는 클로저로 변환해준다.
옵셔널 체이닝과 빠른종료
빠른종료
guard
문의 조건에는Bool
타입이 오므로, 옵셔널 바인딩의 역할을 하지 않을 때도 빠른 종료를 위해 사용할 수 있다.- 쉼표를 사용하여 추가 조건을 나열할 수 있으므로, AND 연산과 같은 결과를 낸다.
- 제어문 전환 명령어를 사용해야 하므로 특정 블록 내부에 위치하지 않는다면 사용이 제한된다.