You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
스위프트에는, 네 종류의 표현식이 있는데
접두사 표현식과, 중위 표현식, 으뜸 표현식, 접미사 표현식이 있다
표현식의 평가는 값을 반환하거나, 사이드 이펙트를 일으키거나 혹은 둘 다 발생하게 된다
접두사 및 중위 표현식은 더 작은 표현식에 연산자를 적용하게 해준다
으뜸 표현식은 개념상 가장 단순한 종류의 표현식으로, 값에 접근하는 방식을 제공한다
접미사 표현식은 접두사 및 중위 표현식과 마찬가지로
함수 호출과 멤버 접근 같은 접미사를 사용하여 더 복잡한 표현식을 제작하게 해준다
각 종류의 표현식은 밑의 절에서 자세하게 알아보자
접두사 표현식
접두사 표현식 (prefix expressions) 은 옵션인 접두사 연산자와 표현식을 조합한다
접두사 연산자는 하나의 인자로, 자신의 뒤에 있는 표현식을 취한다
try 표현식(try expression)은 try 연산자와 그 뒤의 에러를 던질 수 있는 표현식으로 구성된다
형식은 다음과 같다
try expression
try 표현식의 값은 표현식(expression)의 값이 된다
옵셔널-try 표현식(optional-try expression)은 try? 연산자와 그 뒤의 에러를 던질 수 있는 표현식으로 구성된다
형식은 다음과 같다
try? expression
표현식 (expression)이 에러를 던지지 않으면,
옵셔널-try 표현식의 값은 표현식(expression)의 값을 담은 옵셔널이 된다
그 외의 경우, 옵셔널-try 표현식의 값이 nil이다
강제-try 표현식(forced-try expression)은 try! 연산자와 그 뒤의 에러를 던질 수 있는 표현식으로 구성된다
형식은 다음과 같다
try! expression
강제-try 표현식의 값은 표현식(expression)의 값이 된다
표현식(expression) 해당 에러를 던지면, 런타임 에러가 발생한다
중위 연산자의 왼쪽 표현식에 try 나, try?, 또는 try! 를 표시할 땐,
중위 표현식 전체에 try 연산자를 적용한다
그렇더라도, 괄호를 사용하면 연산자의 적용 범위을 명시할 수 있다
// try applies to both function calls
sum =trysomeThrowingFunction()+ anotherThrowingFunction()
// try applies to both function calls
sum =try(someThrowingFunction()+ anotherThrowingFunction())
// Error: try applies only to the first function call
sum =(trysomeThrowingFunction())+ anotherThrowingFunction()
중위 연산자가 할당 연산자거나 try 표현식에 괄호친 게 아니면
중위 연산자 오른쪽에 try 표현식이 다음으로 나타날 순 없다
표현식이 try 와 await 연산자를 둘 다 포함하면, 반드시 try 연산자가 먼저 나타나야 한다
try 와, try? 및 try! 에 대한 더 많은 정보와 사용 예제를 보려면 Error Handling 페이지에서 확인할 수 있다
3) Await 연산자
await 표현식 (await expression)은 await 연산자와 그 뒤의 비동기 연산 결과를 사용하는 표현식으로 구성된다
형식은 다음과 같다
await expression
await 표현식의 값은 표현식 (expression)의 값이 된다
await 로 표시한 표현식을 잠재적인 잠시 멈춤 지점(potential suspension point)이라고 하는데
비동기 함수 실행은 await 로 표시한 각각의 표현식마다 잠시 멈출 수 있게 된다
여기다, 동시적 코드 실행은 다른 어떤 지점에서도 절대 잠시 멈추지 않는다
이는 잠재적인 잠시 멈춤 지점 사이의 코드가 불변성(invariants)을 일시적으로 깨길 요구하는 경우
상태 업데이트를 안전하게 할 수 있도록 그 다음 잠재적인 잠시 멈춤 지점 전에 업데이트를 완료한다는 의미가 된다
await 표현식은 async(priority:operation:) 함수에 전달하는 후행 클로저 같은 비동기 context 안에서만 나타날 수 있다
defer문의 본문이나, 동기 함수 타입의 auto 클로저 안에선 나타날 수 없다
중위 연산자의 왼쪽 표현식에 await 연산자를 표시할 땐,
중위 표현식 전체에 그 연산자를 적용한다
그렇더라도, 괄호를 사용하면 연산자의 적용 범위를 명시할 수 있게 된다
// await applies to both function calls
sum =awaitsomeAsyncFunction()+ anotherAsyncFunction()
// await applies to both function calls
sum =await(someAsyncFunction()+ anotherAsyncFunction())
// Error: await applies only to the first function call
sum =(awaitsomeAsyncFunction())+ anotherAsyncFunction()
중위 연산자가 할당 연산자거나 await 표현식에 괄호친 게 아니면,
중위 연산자 오른쪽에 await 표현식이 바로 나타날 순 없다
표현식이 try 와 await 연산자를 둘 다 포함하면, 반드시 try 연산자가 먼저 나타나야 한다
중위 표현식(Infix Expressions)
중위 표현식 (infix expressions)은 중위 이항 연산자를 왼쪽 및 오른쪽 인자로 취한 표현식과 조합한다
형식은 다음과 같다
구문 해석 시간에, 중위 연산자로 이뤄진 표현식은 평탄한 리스트(flat list)로 나타낸다 - 차원을 축소하여 1 차원화한 리스트
리스트에 연산자 우선 순위를 적용함으로써 트리(tree)로 변형된다
예를 들어, 초기에는 표현식 2 + 3 * 5 가 2, +, 3, *, 및 5 라는 다섯 항목으로 된 평탄한 리스트라고 이해한다
이 후 과정에서 이를 (2 + (3 * 5)) 라는 트리로 변형된다
1) 할당 연산자
할당 연산자 (assignment operator) 는 주어진 표현식에 새 값을 설정한다
형식은 다음과 같다
expression = value
표현식(expression)의 값을 값 (value) 평가로 구한 값으로 설정한다
표현식 (expression)이 튜플이면, 값(value)도 반드시 원소 개수가 동일한 튜플이어야 한다(중첩 튜플은 허용)
각 부분의 값(value)에서 해당 부분의 표현식(expression)으로 할당을 수행한다
예를 들면 다음과 같다
(a, _,(b, c))=("test", 9.45,(12,3))
// a is "test", b is 12, c is 3, and 9.45 is ignored
할당 연산자는 어떤 값도 반환하지 않는다
2) 삼항 조건 연산자(Ternary Conditional Operator)
삼항 조건 연산자 (ternary conditional operator) 는 조건 값을 기초로 하여 주어진 두 값 중 하나를 평가한다
형식은 다음과 같다
condition ? expression used if true : expression used if false
조건 평가(condition)가 true 면,
조건 연산자가 첫 번째 표현식을 평가하고 그 값을 반환한다
그 외 경우, 두 번째 표현식을 평가하고 그 값을 반환한다
사용하지 않는 표현식은 평가하지 않는다
네 개의 타입-변환 연산자가 있는데 is, as, as?, as! 연산자가 있다
형식은 다음과 같다
expression is type
expression as type
expression as? type
expression as! type
is 연산자는 실행 시간에 표현식(expression)을 특정 타입(type)으로 변환할 수 있는 지 검사한다
표현식(expression)이 특정 타입(type)으로 변환할 수 있으면
true를 반환하며 그 외 경우, false를 반환한다
as 연산자는, 올림 변환이나 연동(bridging)같이,
변환이 항상 성공함을 컴파일 시간에 알 수 있을 때 하는 변환이다
올림 변환은 중간 단계의 변수를 쓰지 않고도
표현식을 해당 타입의 부모 타입 인스턴스로 사용하게 해준다
자식 클래스일 경우에는 부모 클래스를 상속한 것이기 때문에 올림 변환은 항상 성공한다
다음의 접근법은 서로 동일한데
func f(_ any:Any){print("Function for Any")}func f(_ int:Int){print("Function for Int")}letx=10f(x)
// Prints "Function for Int"
lety:Any= x
f(y)
// Prints "Function for Any"
f(x asAny)
// Prints "Function for Any"
연동은 새로운 인스턴스를 생성할 필요 없이
String 같은 스위프트 표준 라이브러리 타입의 표현식을
NSString 같이 그에 해당하는 파운데이션(Foundation) 타입으로 사용하게 해준다
연동에 대한 더 많은 정보는, Working with Foundation Types 페이지에서 확인할 수 있다
as? 연산자는 표현식(expression)을 특정 타입(type)으로 조건부 변환한다
as? 연산자는 특정 타입(type)에 대한 옵셔널을 반환한다
런타임에 변환 성공하면 표현식(expression)의 값을 옵셔널로 포장하여 반환하며, 그 외 경우, 반환 값이 nil이 된다
특정 타입(type)으로의 변환이 실패로 보증된 것 또는 성공으로 보증된 것이면, 컴파일 시 에러가 발생하게 된다
as! 연산자는 표현식 (expression) 을 특정 타입(type)으로 강제 변환한다
as! 연산자는 옵셔널 타입이 아닌 특정 타입(type) 값을 반환한다
변환을 실패하면 런타임 에러가 발생하게 된다
ex. x as! T 는 (x as? T)! 와 똑같이 동작한다
타입 변환에 대한 더 많은 정보 및 타입-변환 연산자의 사용 예제는, Type Casting 페이지에서 확인할 수 있다
으뜸 표현식(Primary Expressions)
으뜸 표현식 (primary expressions) 은 가장 기초적인 종류의 표현식이다
그 자체로 표현식으로 사용할 수도 있고,
다른 낱말과 조합하여 접두사 표현식과, 중위 표현식, 및 접미사 표현식을 만들 수도 있다
1) 글자값 표현식(Literal Expression)
글자값 표현식(literal expression)은 문자열이나 수치 값 같은 평범한 글자값이나
배열 및 딕셔너리 글자값, 플레이그라운드(playground) 글자값, 또는 다음의 특수 글자값 중 하나로 구성된다
‘동적 공유 객체 (dynamic shared object; DSO)’는 .dylib 나 .so 같이
현재 실행 중인 동적 연결 라이브러리를 의미한다
이에 대한 더 자세한 내용은, 애플 개발자 문서의 Overview of Dynamic Libraries 항목을 보도록하자
#file의 문자열 값은 언어 버전에 따라 달라지므로 이전 #filePath 동작에서 새 #fileID로 마이그레이션할 수 있다
(스위프트 5.3 이전까지의 #file 은 그 파일의 전체 경로였는데, 앞으로는 파일과 모듈 이름으로 바뀌게 된다)
현재 버전의 스위프트에선, #file는 #filePath 값과 똑같다
미래 버전의 스위프트에선, #file는 #fileID 값과 똑같을 예정이다
미래의 동작을 채택하려면, #file 을 #fileID 나 #filePath 로 적절하게 교체하여 사용하면 된다
미래 버전의 스위프트에선 #file 과 #filePath 를 확실하게 구분할 예정이며
#filePath 를 출하용 프로그램에서만 사용할 것을 권하는데, 이러한 구분은 개인 정보 보호(privacy) 정책과도 관련된다
#fileID 표현식의 문자열 값은 모듈/파일(module/file) 형식인데,
파일(file)은 표현식이 있는 파일 이름이고
모듈(module)은 이 파일이 속해 있는 모듈 이름이다
#filePath 표현식의 문자열 값은 표현식이 있는 파일의 전체 파일-시스템 경로다
Line Control Statement 페이지에서 설명한 것처럼,
#fileID, #filePath 두 값 모두 #sourceLocation으로 바꿀 수 있다
#filePath와 달리, #fileID에는 소스 파일의 전체 경로를 포함하지 않기 때문에,
개인 정보를 더 잘 보호하며 컴파일한 바이너리의 크기가 줄여든다
테스트, 빌드 스크립트 또는 그 외 출하용 프로그램의 일부분이 아닌 코드 밖에선 #filePath의 사용을 피해야 한다
개인이 소유하거나 컴파일하여 바이너리로 변환할 게 아닌 코드엔 #filePath 를 사용하지 않음으로써 개인 정보를 보호할 수 있다
#fileID 표현식 구문을 해석하려면,
첫 번째 빗금(/) 앞의 텍스트는 모듈 이름으로 마지막 빗금 뒤의 텍스트는 파일 이름으로 읽는다
미래에는, MyModule/some/disambiguation/MyFile.swift 같이, 문자열이 여러 개의 빗금을 담고 있을 수도 있다
#function 값은
함수 안에선 해당 함수의 이름,
메소드 안에선 해당 메소드의 이름,
프로퍼티의 게터나 세터 안에선 해당 프로퍼티의 이름,
init 이나 subscript 같은 특수 멤버 안에선 해당 키워드의 이름,
파일의 최상단에선 현재 모듈의 이름이다
함수나 메소드 매개 변수의 기본 값으로 사용할 땐, 호출한 쪽에서 기본 값 표현식을 평가할 때 특수 글자값의 값을 결정한다
func logFunctionName(string:String= #function){print(string)}func myFunction(){logFunctionName() // Prints "myFunction()".
}
배열 글자값(array literal)은 순서 있는 값의 집합체(ordered collection)다
형식은 다음과 같다
[value 1, value 2, ...]
배열의 마지막 표현식 뒤에 쉼표가 있어도 된다
배열 글자값은 [T] 타입인데, 여기서 T는 그 안의 표현식 타입이다
표현식의 타입이 여러 개면, T는 이들의 가장 가까운 공통 상위 타입(closest common supertype)이 된다
빈 배열 글자값은 빈 대괄호 쌍으로 작성하며, 이를 사용하여 특정 타입의 빈 배열을 생성할 수 있다
varemptyArray:[Double]=[]
딕셔너리 글자값(dictionary literal)은 순서 없는 키-값(key-value)쌍의 집합체(unordered collection)다
형식은 다음과 같다
[key 1: value 1, key 2: value 2, ...]
딕셔너리의 마지막 표현식 뒤에 쉼표가 있어도 된다
딕셔너리 글자값은 [Key : Value] 타입인데,
여기서 Key는 키 표현식의 타입이고
Value는 값 표현식의 타입이다
표현식의 타입이 여러 개면, Key와 Value 는 각자 값의 가장 가까운 공통 상위 타입이 된다
빈 딕셔너리 글자값은 빈 배열 글자값과 구별하려고 대괄호 쌍 안에 콜론([:])을 작성한다
빈 딕셔너리 글자 값을 사용하여 특정 키와 값 타입의 빈 딕셔너리 글자 값을 생성할 수 있다
varemptyDictionary:[String:Double]=[:]
플레이그라운드 글자값(playground literal)은 엑스코드(Xcode) 에서 사용하는 것으로
프로그램 편집기 안에서 색상, 파일 또는 이미지를 대화형으로 나타내도록 한다
엑스코드 밖에 있는 평범한 텍스트에선 플레이그라운드 글자값을 특수 글자 값 구문으로 나타낸다
ex. ‘빨간색’ 플레이그라운드 글자 값은 인데,
이를 복사하여 다른 편집기로 옮기면
var color = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 1)과 같은 특수 글자값 구문을 써서 나타낸다
값 타입의 mutating 메소드 안에선,
해당 값 타입의 새로운 인스턴스를 self 에 할당할 수 있다
예를 들면 다음과 같다
structPoint{varx= 0.0,y= 0.0
mutatingfunc moveBy(x deltaX:Double, y deltaY:Double){self=Point(x: x + deltaX, y: y + deltaY)}}
3) 상위 클래스 표현식(Superclass Expression)
상위 클래스 표현식(superclass expression)은 클래스를 자신의 상위 클래스와 상호 작용하게 해준다
형식은 다음 중 하나다
super.member name
super[subscript index]
super.init(initializer arguments)
첫 번째 형식으로는 상위 클래스의 멤버에 접근한다
두 번째 형식으로 상위 클래스의 서브 스크립트 구현에 접근한다
세 번째 형식으로 상위 클래스의 이니셜라이저에 접근한다
하위 클래스의 멤버와, 서브 스크립트 및 이니셜라이저 구현 안에
상위 클래스 표현식을 사용하면 자신의 상위 클래스 안의 구현을 사용할 수 있다
4) 클로저 표현식
클로저 표현식(closure expression)은,
다른 프로그래밍 언어에선 람다(lambda)나 익명 함수(anonymous function)라고도 하는클로저를 생성한다
함수 선언처럼 클로저도 구문을 담으며
자신을 둘러싼 영역의 상수와 변수를 붙잡게(capturoing) 된다
형식은 다음과 같다
함수 호출 부분에서 곧바로 클로저를 사용할 때 처럼, 변수나 상수에 저장하지 않고도 클로저를 사용할 수 있다
위 코드에서 myFunction에 전달한 클로저 표현식이 곧바로 사용하는 예시다
그 결과, 클로저 표현식이 벗어나는 건지 벗어나지 않는 건지는 표현식의 주위 상황에 달려 있게 된다
표현식 자체가 아니라 표현식을 호출하는 쪽에 달려 있다
곧바로 호출하거나 벗어나지 않는 함수 인수로 전달한 클로저 표현식은 벗어나지 않게 된다
그 외 경우의 클로저 표현식은 벗어나게 된다
기본적으로, 클로저 표현식은 자기 주위 영역의 상수와 변수 값을 강한 참조로 붙잡는다
캡쳐 리스트를 사용하면 클로저에서 값을 붙잡는 방법을 명시적으로 제어할 수 있다
캡쳐 리스트는 매개 변수 목록 앞에 쉼표로 구분한 표현식 목록을 대괄호로 둘러싸서 작성한다
캡쳐 리스트를 사용하면 매개 변수 이름, 매개 변수 타입 및 반환 타입을 생략한 경우라도 반드시 in 키워드를 사용해야 한다
캡쳐 리스트 안의 요소는 클로저 생성 때 초기화한다
붙잡을 목록의 각 요소마다, 한 상수를 주위 영역 안의 동일 이름인 상수나 변수 값으로 초기화한다
아래 코드 예제에서, a는 캡쳐 리스트에 포함하지만 b는 아니어서 서로 다르게 동작하게 된다
vara=0varb=0letclosure={[a]inprint(a, b)}
a =10
b =10closure()
// Prints "0 10"
a라는 이름은 주위 영역 안의 변수와 클로저 영역 안의 상수라는 서로 다른 두 개가 있지만,
b라는 이름의 변수는 하나뿐이다
안쪽 영역의 a 는 클로저 생성 할당 때 바깥 영역의 a 값으로 초기화하지만,
이러한 값 타입은 어떤 특수한 방식으로도 연결되어 참조 된 것이 아니다
이는 바깥 영역의 a 값을 바꿔도 안쪽 영역의 a 값에 영향을 주지 않으며,
클로저 안의 a 를 바꿔도 클로저 밖의 a 값에 영향을 주지 않는다
이와 대조적으로, b 라는 이름의 변수는 바깥 영역의 b 단 하나만 있어서
클로저 안팎에서 할당에 따라서 바뀌어지는 걸 양쪽 다 볼 수 있다
캡쳐된 변수의 타입이 참조 의미 구조를 가질 땐 이런 구별이 보이지 않는다
예를 들어, 아래 코드에는 바깥 영역의 변수와 안쪽 영역의 두 개의 상수 x 가 있지만,
참조 의미 구조이기 때문에 둘 다 동일한 객체를 참조하게 된다
classSimpleClass{varvalue:Int=0}varx=SimpleClass()vary=SimpleClass()letclosure={[x]inprint(x.value, y.value)}
x.value =10
y.value =10closure()
// Prints "10 10"
표현식 값의 타입이 클래스면 붙잡을 목록 안의 표현식에 weak 나 unowned 를 표시하여
표현식 값에 대한 약한 또는 소유하지 않는 참조를 붙잡을 수 있다
암시적 멤버 표현식(implicit member expression)은 열거체 case나 타입 메소드 같이,
암시적 타입을 추론할 수 있는 상황에서 타입 멤버에 접근하는 단축 방식이다
형식은 다음과 같다
.member name
varx=MyEnumeration.someValue
x =.anotherValue
추론한 타입이 옵셔널이면, 암시적 멤버 표현식 안에서 옵셔널 아닌 타입의 멤버도 사용할 수 있다
varsomeOptional:MyEnumeration?=.someValue
암시적 멤버 표현식 뒤엔 접미사 표현식에 나열된 접미사 연산자나 다른 접미사 구문이 있을 수 있다
이를 연쇄된 암시적 멤버 표현식(chained implicit member expression)이라고 한다
연쇄한 모든 접미사 표현식이 동일 타입을 가지는 게 일반적이지만,
유일한 요구 사항은 연결된 암시적 멤버 표현식 전체를 컨텍스트에서 암시하는 형식으로 변환할 수 있어야 한다는 것이다
특히, 암시 타입이 옵셔널이면 옵셔널 아닌 타입의 값을 사용할 수도 있고,
암시 타입이 클래스 타입이면 자신의 하위 클래스 타입 중 하나의 값을 사용할 수도 있다
예를 들면 다음과 같다
위 코드에서,
x 의 타입은 자신의 암시 타입과 정확하게 일치하고,
y 의 타입은 SomeClass 에서 SomeClass? 로 변환 가능하며,
z 의 타입은 SomeSubclass 에서 SomeClass 로 변환 가능하다
7) 괄호 표현식(Parenthesized Expression)
괄호 표현식(parenthesized expression)은 표현식을 괄호로 둘러싸서 구성한다
괄호를 사용하여 표현식을 명시적으로 그룹지으면
연산의 우선 순위를 지정할 수 있다
괄호 그룹짓기는 표현식의 타입을 바꾸지 않는데, 예를 들어, (1)의 타입은 단순히 Int가 된다
8) 튜플 표현식
튜플 표현식(tuple expression)은 쉼표로 구분한 표현식 목록을 괄호로 둘러싸서 구성한다
각각의 표현식엔 옵션으로 자기 앞에 콜론(:)으로 구분한 식별자가 있을 수 있다
형식은 다음과 같다
튜플 표현식에 있는 각각의 식별자는
튜플 표현식 영역 안에서 반드시 유일해야 한다
중첩 튜플 표현식에선 동일 중첩 수준의 식별자는 반드시 유일해야 한다
예를 들어, (a: 10, a: 20) 은 무효한데 동일한 수준에 이름표 a가 두 번 나타나기 때문이다
하지만, (a: 10, b: (a: 1, x: 2)) 는 유효하다
a가 두 번 나타나긴 하지만, 한 번은 바깥쪽 튜플에서 한 번은 안쪽 튜플에서 나타난다
튜플 표현식은 0개의 표현식을 담거나,
둘 이상의 표현식을 담을 수 있다
괄호 안에 표현식이 단 하나면 괄호 표현식으로 인식한다
스위프트에서 빈 튜플 표현식과 빈 튜플 타입은 둘 다 () 로 작성한다
Void는 ()의 타입 별명(type alias)이기 때문에, 이걸 써서 빈 튜플 타입을 작성할 수 있다
하지만, 모든 타입 별명과 마찬가지로 Void 는 항상 타입으로 사용한다
이를 써서 빈 튜플 표현식을 작성할 순 없다 - 그렇기 때문에 Void -> Void 같은 함수 타입은 없으며 () -> Void 라고 해야 한다
8) 와일드카드 표현식
와일드카드 표현식(wildcard expression)을 사용하면 할당 중에 값을 명시적으로 무시한다
예를 들어, 다음 할당에선 10은 x에 할당하고 20은 무시된다
(x, _)=(10,20)
// x is 10, and 20 is ignored
9) 키-경로 표현식(Key-Path Expression)
키-경로 표현식(key-path expression)은 타입의 프로퍼티이나 서브 스크립트를 참조한다
키-값 관찰 같은 동적 프로그래밍 임무에서 키-경로 표현식을 사용하는데
형식은 다음과 같다
\type name.path
타입 이름(type name)은 String 이나 [Int], 또는 Set 같이
어떤 일반화 매개 변수도 포함한 고정 타입의 이름이다
경로(path)는 프로퍼티 이름과, 서브 스크립트, 옵셔널 체이닝 표현식 및 강제 언래핑 표현식으로 구성된다
이 각각의 키-경로 성분은 필요한 만큼 많이 어떤 순서로든 반복할 수 있다
키-경로로 값에 접근하려면 subscript(keyPath:) 서브 스크립트를 통해 키 경로를 전달하면 되는데,
이는 모든 타입에서 사용 가능하다
예를 들면 다음과 같다
structSomeStructure{varsomeValue:Int}lets=SomeStructure(someValue:12)letpathToProperty= \SomeStructure.someValue
letvalue=s[keyPath: pathToProperty]
// value is 12
타입 추론이 암시 타입을 결정할 수 있는 상황에선 타입 이름(type name)을 생략할 수 있다
다음 코드는 \SomeClass.someProperty 대신 \.someProperty 를 사용한다
경로(path)는 프로퍼티의 프로퍼티를 참조하기 위해 마침표로 구분된 여러 프로퍼티 이름이 포함될 수 있다
다음 코드는 \OuterStructure.outer.someValue 키 경로 표현식을 사용하여
OuterStructure 타입의 outer 프로퍼티에 있는 someValue 프로퍼티에 접근한다
structOuterStructure{varouter:SomeStructureinit(someValue:Int){self.outer =SomeStructure(someValue: someValue)}}letnested=OuterStructure(someValue:24)letnestedKeyPath= \OuterStructure.outer.someValue
letnestedValue=nested[keyPath: nestedKeyPath]
// nestedValue is 24
경로(path)는 서브 스크립트의 매개 변수 타입이 Hashable 프로토콜을 준수하면
대괄호를 써서 서브 스크립트를 포함할 수 있다
다음 예제는 키 경로에서 서브 스크립트를 써서 배열의 두 번째 원소에 접근한다
letgreetings=["hello","hola","bonjour","안녕"]letmyGreeting=greetings[keyPath: \[String].[1]]
// myGreeting is 'hola'
서브 스크립트에서 사용할 수 있는 값은 이름 붙인 값 또는 글자값이다
키 경로는 값 의미 구조로 값을 캡쳐한다
다음 코드는 키-경로 표현식과 클로저 둘 다에서
index 변수로 greetings 배열의 세 번째 원소에 접근한다
index가 수정되면, 키-경로 표현식이 여전히 세 번째 원소를 참조하는 동안 클로저는 새롭게 수정된 index를 사용한다
varindex=2letpath= \[String].[index]letfn:([String])->String={ strings instrings[index]}print(greetings[keyPath: path])
// Prints "bonjour"
print(fn(greetings))
// Prints "bonjour"
// Setting 'index' to a new value doesn't affect 'path'
index +=1print(greetings[keyPath: path])
// Prints "bonjour"
// Because 'fn' closes over 'index', it uses the new value
print(fn(greetings))
// Prints "안녕"
경로(path)는 옵셔널 체이닝과 강제 언래핑을 사용할 수 있다
다음 코드는 키 경로에서 옵셔널 체이닝을 써서 옵셔널 문자열 안의 프로퍼티에 접근한다
letfirstGreeting:String?= greetings.first
print(firstGreeting?.count asAny)
// Prints "Optional(5)"
// Do the same thing using a key path.
letcount=greetings[keyPath: \[String].first?.count]print(count asAny)
// Prints "Optional(5)"
키 경로의 성분을 섞어서 맞춰보면 타입 안에 깊숙히 중첩된 값에 접근할 수 있다
다음 코드는 이러한 성분을 조합한 키-경로 표현식을 사용하여
배열 딕셔너리의 서로 다른 값과 프로퍼티에 접근한다
letinterestingNumbers=["prime":[2,3,5,7,11,13,17],"triangular":[1,3,6,10,15,21,28],"hexagonal":[1,6,15,28,45,66,91]]print(interestingNumbers[keyPath: \[String:[Int]].["prime"]]asAny)
// Prints "Optional([2, 3, 5, 7, 11, 13, 17])"
print(interestingNumbers[keyPath: \[String:[Int]].["prime"]![0]])
// Prints "2"
print(interestingNumbers[keyPath: \[String:[Int]].["hexagonal"]!.count])
// Prints "7"
print(interestingNumbers[keyPath: \[String:[Int]].["hexagonal"]!.count.bitWidth])
// Prints "64"
보통은 함수나 클로저를 제공할 상황에서 키 경로 표현식을 사용할 수 있다
특히, (SomeType) -> Value 타입의 함수나 클로저 대신,
근원 타입은 SomeType이고 경로는 Value 타입의 값을 만드는 키 경로 표현식을 사용할 수도 있다
structTask{vardescription:Stringvarcompleted:Bool}vartoDoList=[Task(description:"Practice ping-pong.", completed: false),Task(description:"Buy a pirate costume.", completed: true),Task(description:"Visit Boston in the Fall.", completed: false),]
// Both approaches below are equivalent. - ["Buy a pirate costume."]
letdescriptions= toDoList.filter(\.completed).map(\.description)letdescriptions2= toDoList.filter{ $0.completed }.map{ $0.description }
키 경로 표현식의 어떤 사이드 이펙트든 표현식을 평가하는 시점에만 평가한다
예를 들어, 키 경로 표현식의 서브 스크립트 안에 함수 호출을 만들면
표현식 평가 부분에서 단 한번만 함수를 호출할 뿐, 키 경로를 사용할 때마다 호출하진 않는다
func makeIndex()->Int{print("Made an index")return0}
// The line below calls makeIndex().
lettaskKeyPath= \[Task][makeIndex()]
// Prints "Made an index"
// Using taskKeyPath doesn't call makeIndex() again.
letsomeTask=toDoList[keyPath: taskKeyPath]
프로퍼티의 게터에 대한 셀렉터를 만들 때
프로퍼티 이름은 변수 또는 상수 프로퍼티에 대한 참조일 수 있다
반대로, 프로퍼티의 세터에 대한 셀렉터를 만들 때
프로퍼티 이름은 반드시 변수 프로퍼티에 대한 참조여야 한다
메소드 이름(method name)은 이름은 공유하지만 타입 서명은 다른 메소드 사이의 모호함을 없애는 as 연산자뿐 아니라,
그룹을 짓기 위한 괄호도 담을 수 있다
타입 서명이 다르다는 건
중복 정의(overloading)하여 함수 이름은 같지만, 매개 변수 및 반환 타입을 포함한 함수의 타입 자체는 다른 메소드들 의미한다
예를 들면 다음과 같다
키-경로 문자열 표현식은 문자열에 접근해서,
키-값 코딩과 키-값 관찰 API 에서 사용할, 오브젝티브-C의 프로퍼티를 참조하게 해준다
‘키-경로 문자열 표현식(key-path string expression)’ 은
‘키-경로 표현식(key-path expression)’ 을 오브젝티브-C 의 프로퍼티에 사용하기 위한 방법이다
#keyPath(property name)
프로퍼티 이름(property name)은 반드시 오브젝티브-C 런타임에서 사용 가능한 프로퍼티를 참조해야 한다
컴파일 시점에 키-경로 문자열 표현식을 문자열 글자값으로 교체한다
예를 들면 다음과 같다
classSomeClass:NSObject{@objcvarsomeProperty:Intinit(someProperty:Int){self.someProperty = someProperty
}}letc=SomeClass(someProperty:12)letkeyPath= #keyPath(SomeClass.someProperty)
if let value = c.value(forKey: keyPath){print(value)}
// Prints "12"
클래스 안에서 키-경로 문자열 표현식을 사용할 땐,
클래스 이름 없이 그냥 프로퍼티 이름만 작성하여, 해당 클래스의 프로퍼티를 참조할 수 있다
extensionSomeClass{func getSomeKeyPath()->String{return #keyPath(someProperty)}}print(keyPath == c.getSomeKeyPath())
// Prints "true"
런타임이 아닌 컴파일 시점에 키 경로 문자열을 생성하기 때문에,
컴파일러는 프로퍼티가 존재하는지 그리고 오브젝티브-C 런타임에 노출되는지 확인할 수 있다
함수 호출 표현식(function call expression)은 함수 이름과
그 뒤의 소괄호 안에 있는 쉼표로 구분한 함수 매개 변수 목록으로 구성된다
함수 호출 표현식의 형식은 다음과 같다
function name(argument value 1, argument value 2)
함수 이름(function name)은 그 값이 함수 타입인 어떤 표현식이든 가능하다
함수 정의가 자신의 인수 라벨을 포함하면,
함수 호출도 반드시 자신의 인자 값 앞에 콜론(:)으로 구분한 이름을 포함해야 한다
이런 종류의 함수 호출 표현식 형식은 다음과 같다
function name(argument name 1: argument value 1, argument name 2: argument value 2)
함수 호출 표현식은 닫는 괄호 바로 뒤에 클로저 표현식 형식의 후행 클로저(trailing closures)를 포함할 수 있다
후행 클로저는 괄호 안 마지막 매개 변수 뒤에 추가된 함수 매개 변수로 여겨진다
첫 번째 클로저 표현식엔 이름표를 붙이지 않으며
추가적인 어떤 클로저 표현식이든 앞에 자신의 인수 라벨을 붙인다
아래 예제는 후행 클로저 구문을 사용한 것과 하지 않은 서로 같은 함수 호출을 보여준다
// someFunction takes an integer and a closure as its arguments
someFunction(x: x, f:{ $0 ==13})someFunction(x: x){ $0 ==13}
// anotherFunction takes an integer and two closures as its arguments
anotherFunction(x: x, f:{ $0 ==13}, g:{print(99)})anotherFunction(x: x){ $0 ==13} g:{print(99)}
후행 클로저가 함수의 유일한 인자면, 소괄호를 생략할 수 있다
// someMethod takes a closure as its only argument
myData.someMethod(){ $0 ==13}
myData.someMethod{ $0 ==13}
후행 클로저를 매개 변수에 포함하려고, 다음처럼 컴파일러가 함수 매개 변수를 왼쪽에서 오른쪽으로 검토한다
후행 클로저
매개 변수
행동
인수 라벨 있음
인수 라벨 있음
인수 라벨이 똑같으면, 클로저가 매개 변수와 일치하며 그 외 경우, 매개 변수를 건너 뛴다
인수 라벨 있음
인수 라벨 없음
매개 변수를 건너 뛴다
인수 라벨 없음
인수 라벨 관계 없음
아래 정의한 것처럼 매개 변수의 구조가 함수 타입과 닮았으면, 클로저가 매개 변수와 일치하며, 그 외 경우, 매개 변수를 건너 뛴다
후행 클로저는 자신과 일치한 매개 변수에 인수로 전달된다
검색 프로세스 중에 건너 뛴 매개 변수엔 인수를 전달하지 않는다
예를 들어, 기본 매개 변수를 사용할 수 있는 경우가 있다
일치한 걸 찾은 후에 그 다음 후행 클로저 및 그 다음 매개 변수를 계속 검색한다
일치 프로세스가 끝나면 반드시 모든 후행 클로저에 일치하는 항목이 있어야 한다
매개 변수가 In-Out 매개 변수가 아니면서,
다음 중 하나라면 매개 변수가 함수 타입과 구조가 닮은(structually resembles)거로 여긴다
(Bool) -> Int 같이, 타입이 함수 타입인 매개 변수
@autoclosure ()-> ((Bool)-> Int) 같이, 포장한 표현식의 타입이 함수 타입인 자동 클로저 매개 변수
((Bool) -> Int)... 같이, 배열 원소 타입이 함수 타입인 가변 매개 변수
Optional<(Bool) -> Int> 같이, 타입이 한 겹 이상의 옵셔널로 포장된 매개 변수
(Optional<(Bool) -> Int>) ... 같이, 이렇게 허용한 타입들의 조합인 매개 변수
후행 클로저가 함수 타입과 타입이 닮은 구조인 매개 변수와 일치하나,
함수가 아닐 땐, 필요에 따라 클로저가 래핑된다
예를 들어, 매개 변수 타입이 옵셔널 타입이면 자동으로 클로저를 Optional로 포장한다
이런 맞춤을 오른쪽에서 왼쪽으로 실시했던 스위프트 5.3 이전 버전의 코드를 쉽게 마이그레이션하도록,
컴파일러는 왼쪽에서 오른쪽과 오른쪽에서 왼쪽 순서 둘 다 검사한다
검사한 방향에 따라 결과가 다르게 생성되면,
예전의 오른쪽에서 왼쪽 순서를 사용하고 컴파일러가 경고를 생성한다
미래 버전의 스위프트는 항상 왼쪽에서 오른쪽 순서를 사용할 예정이다
typealiasCallback=(Int)->Intfunc someFunction(firstClosure:Callback?=nil,
secondClosure:Callback?=nil){letfirst=firstClosure?(10)letsecond=secondClosure?(20)print(first ??"-", second ??"-")}someFunction() // Prints "- -"
someFunction{return $0 +100} // Ambiguous
someFunction{return $0 } secondClosure:{return $0 } // Prints "10 20"
위 예제에서, “ambiguous” 으로 표시한 함수 호출은 “- 120” 을 인쇄하며 스위프트 5.3에서 컴파일러 경고를 표시한다
미래 버전의 스위프트는 “110 -“ 을 인쇄하게 된다
이 암시적 변환들로 생성한 포인터는 함수 호출 지속 시간에만 유효하다
정의되지 않은 동작을 피하려면, 함수 호출이 끝난 후 절대로 코드가 포인터를 물고 있지 않도록 보장해야 한다
배열을 안전하지 않은 포인터로 암시적 변환할 때,
스위프트는 필요에 따라 배열을 변환하거나 복사함으로써 배열 저장 공간이 딱 붙어있도록 보장한다
예를 들어, 저장 공간에 대한 API 계약이 없는 NSArray 하위 클래스에서 Array 로 연동한 배열에 해당 구문을 사용할 수 있다
배열 저장 공간이 이미 딱 붙어 있음을 보증하여 암시적 변환이 절대 이 작업을 할 필요가 없다면,
Array 대신 ContiguousArray를 사용한다
withUnsafePointer(to:) 같은 명시적 함수 대신 &를 사용하야 In-Out 매개 변수처럼 활용하는 건,
특히 함수가 여러가지 포인터 인수를 취할 때의, Low 레벨 C 함수 호출을 더 읽기 쉽게 만들 수 있다
하지만, 다른 스위프트 코드의 함수를 호출할 땐 안전하지 않은 API를 명시적으로 사용하는 대신 &를 사용하는 걸 피하도록 해야한다
3) 이니셜라이저 표현식
이니셜라이저 표현식(initializer expression)은 타입 이니셜라이저에 대한 접근을 제공한다
형식은 다음과 같다
expression.init(initializer arguments)
함수 호출 표현식에서 이니셜라이저 표현식을 사용하여 타입의 새로운 인스턴스를 초기화한다
이니셜라이저 표현식을 사용하여 상위 클래스 이니셜라이저로 일을 위임하기(delegate)도 한다
classSomeSubClass:SomeSuperClass{overrideinit(){
// subclass initialization goes here
super.init()}}
함수 같이 이니셜라이저를 값처럼 사용할 수도 있다
예를 들면 다음과 같다
// Type annotation is required because String has multiple initializers.
letinitializer:(Int)->String=String.init
letoneTwoThree=[1,2,3].map(initializer).reduce("",+)print(oneTwoThree)
// Prints "123"
타입 이름을 정하면 이니셜라이저 표현식을 사용하지 않고 타입 초기자에 접근할 수 있다
다른 모든 경우엔, 반드시 이니셜라이저 표현식을 사용해야 한다
명시적 멤버 표현식(explicit member expression)은 이름 붙인 타입이나, 튜플, 또는 모듈의 멤버에 대한 접근을 허용한다
이는 항목과 자신의 멤버 식별자 사이의 마침표(.)로 구성된다
expression.member name
이름 붙인 타입의 멤버는 타입의 선언이나 익스텐션 부분에서 이름이 붙는다
예를 들면 다음과 같다
classSomeClass{varsomeProperty=42}letc=SomeClass()lety= c.someProperty // Member access
튜플의 멤버는 암시적으로,
자신이 나타나는 순서대로 0 부터 시작하는 정수로 이름이 붙는다
예를 들면 다음과 같다
vart=(10,20,30)
t.0= t.1
// Now t is (20, 20, 30)
모듈의 멤버는 그 모듈의 최상단 선언들에 접근한다
dynamicMemberLookup 특성으로 선언한 타입은, Attributes에서 설명한 것처럼,
런타임에 찾아 보는 멤버를 포함한다
함수 이름이 같고, 자신의 인수 이름만 다른 메소드나 이니셜라이저를 구별하려면,
괄호 안에 인수 이름을 포함하고, 각각의 인자 이름 뒤에 콜론(:)을 붙인다
이름 없는 인수엔 밑줄(_, 와일드카드)를 작성한다
중복 정의한 메소드를 구별하려면 타입 명시 주석을 사용한다
예를 들면 다음과 같다
강제 해제 값 표현식(forced-value expression)은 nil이 아닌 게 확실한 옵셔널 값의 래핑을 풀어낸다
형식은 다음과 같다
expression!
표현식(expression)값이 nil 이 아니면, 옵셔널 값의 포장을 풀고 옵셔널-아닌 해당 타입으로 반환된다
그 외 경우, 런타임 에러가 발생한다
강제 해제 값 표현식의 언래핑된 값은
값 그 자체를 변경하거나
값의 멤버 중 하나에 할당함으로써 수정할 수 있다
예를 들면 다음과 같다
varx:Int?=0
x! +=1
// x is now 1
varsomeDictionary=["a":[1,2,3],"b":[10,20]]someDictionary["a"]![0]=100
// someDictionary is now ["a": [100, 2, 3], "b": [10, 20]]
8) 옵셔널 체이닝 표현식(Optional-Chaining Expression)
옵셔널 체이닝 표현식(optional-chaining expression)은
접미사 표현식에서 옵셔널 값을 사용하기 위한 단순화 구문을 제공한다
형식은 다음과 같다
expression?
접미사 ? 연산자는 표현식 값을 바꾸지 않고도 표현식을 옵셔널 체이닝 표현식으로 만든다
옵셔널 체이닝 표현식은 반드시 접미사 표현식 안에 있어야 하며,
접미사 표현식을 특수한 방식으로 평가하게 한다
옵셔널 체이닝 표현식 값이 nil 이면,
접미사 표현식 안의 모든 다른 연산을 무시하고
전체 접미사 표현식을 nil 로 평가한다
옵셔널 체이닝 표현식 값이 nil 이 아니면,
옵셔널 체이닝 표현식 값의 포장을 풀고
이를 써서 접미사 표현식의 나머지 부분을 평가한다
어느 경우든, 접미사 표현식 값은 여전히 옵셔널 타입이다
옵셔널 체이닝 표현식을 담은 접미사 표현식이 다른 접미사 표현식 안에 중첩되어 있으면,
가장 바깥쪽 표현식만 옵셔널 타입을 반환한다 - 옵셔널에 옵셔널을 중첩하지는 않는다
아래 예제에서 c가 nil 이 아닐 때, 값의 포장을 풀고 이를 써서 .property 를 평가한 후
해당 값으로 .performAction() 을 평가한다
전체 표현식 c?.property.performAction() 의 값은 옵셔널 타입이게 된다
varresult:Bool?
if let unwrappedC = c {
result = unwrappedC.property.performAction()}
옵셔널 체이닝 표현식의 언래핑한 값은
값 그 자체를 변경하거나 값의 멤버 중 하나에 할당함으로써 수정할 수 있다
옵셔널 체이닝 표현식 값이 nil 이면,
할당 연산자의 오른쪽 표현식을 평가하지 않는다
예를 들면 다음과 같다
func someFunctionWithSideEffects()->Int{return42 // No actual side effects.
}varsomeDictionary=["a":[1,2,3],"b":[10,20]]someDictionary["not here"]?[0]=someFunctionWithSideEffects()
// someFunctionWithSideEffects isn't evaluated
// someDictionary is still ["a": [1, 2, 3], "b": [10, 20]]
someDictionary["a"]?[0]=someFunctionWithSideEffects()
// someFunctionWithSideEffects is evaluated and returns 42
// someDictionary is now ["a": [42, 2, 3], "b": [10, 20]]
The text was updated successfully, but these errors were encountered:
스위프트에는, 네 종류의 표현식이 있는데
접두사 표현식과, 중위 표현식, 으뜸 표현식, 접미사 표현식이 있다
표현식의 평가는 값을 반환하거나, 사이드 이펙트를 일으키거나 혹은 둘 다 발생하게 된다
접두사 및 중위 표현식은 더 작은 표현식에 연산자를 적용하게 해준다
으뜸 표현식은 개념상 가장 단순한 종류의 표현식으로, 값에 접근하는 방식을 제공한다
접미사 표현식은 접두사 및 중위 표현식과 마찬가지로
함수 호출과 멤버 접근 같은 접미사를 사용하여 더 복잡한 표현식을 제작하게 해준다
각 종류의 표현식은 밑의 절에서 자세하게 알아보자
접두사 표현식
접두사 표현식 (prefix expressions) 은 옵션인 접두사 연산자와 표현식을 조합한다
접두사 연산자는 하나의 인자로, 자신의 뒤에 있는 표현식을 취한다
연산자 동작에 대한 정보는
Basic Operators와 Advanced Operators 페이지를 보면 더 알 수 있다
스위프트 표준 라이브러리에서 제공하는 연산자에 대한 정보는, Operator Declarations 페이지를 보면 더 알 수 있다
1) In-Out 표현식
In-Out 표현식은 함수 호출 표현식에 In-Out 인수로 전달한 변수를 표시한다
In-Out 매개 변수에 대한 더 많은 정보와 예제를 보려면,
In-Out Parameters 페이지를 통해 알 수 있다
In-Out 표현식은 뒤에 나올 Implicit Conversion to a Pointer Type 장에서 설명하는 것 처럼
포인터가 필요한 상황에서 포인터 아닌 인수를 제공할 때도 사용한다
2) Try 연산자
try 표현식(try expression)은 try 연산자와 그 뒤의 에러를 던질 수 있는 표현식으로 구성된다
형식은 다음과 같다
try 표현식의 값은 표현식(expression)의 값이 된다
옵셔널-try 표현식(optional-try expression)은 try? 연산자와 그 뒤의 에러를 던질 수 있는 표현식으로 구성된다
형식은 다음과 같다
표현식 (expression)이 에러를 던지지 않으면,
옵셔널-try 표현식의 값은 표현식(expression)의 값을 담은 옵셔널이 된다
그 외의 경우, 옵셔널-try 표현식의 값이 nil이다
강제-try 표현식(forced-try expression)은 try! 연산자와 그 뒤의 에러를 던질 수 있는 표현식으로 구성된다
형식은 다음과 같다
강제-try 표현식의 값은 표현식(expression)의 값이 된다
표현식(expression) 해당 에러를 던지면, 런타임 에러가 발생한다
중위 연산자의 왼쪽 표현식에 try 나, try?, 또는 try! 를 표시할 땐,
중위 표현식 전체에 try 연산자를 적용한다
그렇더라도, 괄호를 사용하면 연산자의 적용 범위을 명시할 수 있다
중위 연산자가 할당 연산자거나 try 표현식에 괄호친 게 아니면
중위 연산자 오른쪽에 try 표현식이 다음으로 나타날 순 없다
표현식이 try 와 await 연산자를 둘 다 포함하면, 반드시 try 연산자가 먼저 나타나야 한다
try 와, try? 및 try! 에 대한 더 많은 정보와 사용 예제를 보려면
Error Handling 페이지에서 확인할 수 있다
3) Await 연산자
await 표현식 (await expression)은 await 연산자와 그 뒤의 비동기 연산 결과를 사용하는 표현식으로 구성된다
형식은 다음과 같다
await 표현식의 값은 표현식 (expression)의 값이 된다
await 로 표시한 표현식을 잠재적인 잠시 멈춤 지점(potential suspension point)이라고 하는데
비동기 함수 실행은 await 로 표시한 각각의 표현식마다 잠시 멈출 수 있게 된다
여기다, 동시적 코드 실행은 다른 어떤 지점에서도 절대 잠시 멈추지 않는다
이는 잠재적인 잠시 멈춤 지점 사이의 코드가 불변성(invariants)을 일시적으로 깨길 요구하는 경우
상태 업데이트를 안전하게 할 수 있도록 그 다음 잠재적인 잠시 멈춤 지점 전에 업데이트를 완료한다는 의미가 된다
await 표현식은 async(priority:operation:) 함수에 전달하는 후행 클로저 같은 비동기 context 안에서만 나타날 수 있다
defer문의 본문이나, 동기 함수 타입의 auto 클로저 안에선 나타날 수 없다
중위 연산자의 왼쪽 표현식에 await 연산자를 표시할 땐,
중위 표현식 전체에 그 연산자를 적용한다
그렇더라도, 괄호를 사용하면 연산자의 적용 범위를 명시할 수 있게 된다
중위 연산자가 할당 연산자거나 await 표현식에 괄호친 게 아니면,
중위 연산자 오른쪽에 await 표현식이 바로 나타날 순 없다
표현식이 try 와 await 연산자를 둘 다 포함하면, 반드시 try 연산자가 먼저 나타나야 한다
중위 표현식(Infix Expressions)
중위 표현식 (infix expressions)은 중위 이항 연산자를 왼쪽 및 오른쪽 인자로 취한 표현식과 조합한다
형식은 다음과 같다
해당 연산자 동작에 대한 정보는, Basic Operators와 Advanced Operators 페이지를 통해 더 확인할 수 있다
스위프트 표준 라이브러리에서 제공하는 연산자에 대한 정보는, Operator Declarations 페이지에서 확인 할 수 있다
1) 할당 연산자
할당 연산자 (assignment operator) 는 주어진 표현식에 새 값을 설정한다
형식은 다음과 같다
표현식(expression)의 값을 값 (value) 평가로 구한 값으로 설정한다
표현식 (expression)이 튜플이면, 값(value)도 반드시 원소 개수가 동일한 튜플이어야 한다(중첩 튜플은 허용)
각 부분의 값(value)에서 해당 부분의 표현식(expression)으로 할당을 수행한다
예를 들면 다음과 같다
할당 연산자는 어떤 값도 반환하지 않는다
2) 삼항 조건 연산자(Ternary Conditional Operator)
삼항 조건 연산자 (ternary conditional operator) 는 조건 값을 기초로 하여 주어진 두 값 중 하나를 평가한다
형식은 다음과 같다
조건 평가(condition)가 true 면,
조건 연산자가 첫 번째 표현식을 평가하고 그 값을 반환한다
그 외 경우, 두 번째 표현식을 평가하고 그 값을 반환한다
사용하지 않는 표현식은 평가하지 않는다
삼항 조건 연산자의 사용 예제는, Ternary Conditional Operator 페이지에서 확인할 수 있다
3) 타입-변환 연산자(Type-Casting Operators)
네 개의 타입-변환 연산자가 있는데 is, as, as?, as! 연산자가 있다
형식은 다음과 같다
is 연산자는 실행 시간에 표현식(expression)을 특정 타입(type)으로 변환할 수 있는 지 검사한다
표현식(expression)이 특정 타입(type)으로 변환할 수 있으면
true를 반환하며 그 외 경우, false를 반환한다
as 연산자는, 올림 변환이나 연동(bridging)같이,
변환이 항상 성공함을 컴파일 시간에 알 수 있을 때 하는 변환이다
올림 변환은 중간 단계의 변수를 쓰지 않고도
표현식을 해당 타입의 부모 타입 인스턴스로 사용하게 해준다
자식 클래스일 경우에는 부모 클래스를 상속한 것이기 때문에 올림 변환은 항상 성공한다
다음의 접근법은 서로 동일한데
연동은 새로운 인스턴스를 생성할 필요 없이
String 같은 스위프트 표준 라이브러리 타입의 표현식을
NSString 같이 그에 해당하는 파운데이션(Foundation) 타입으로 사용하게 해준다
연동에 대한 더 많은 정보는, Working with Foundation Types 페이지에서 확인할 수 있다
as? 연산자는 표현식(expression)을 특정 타입(type)으로 조건부 변환한다
as? 연산자는 특정 타입(type)에 대한 옵셔널을 반환한다
런타임에 변환 성공하면 표현식(expression)의 값을 옵셔널로 포장하여 반환하며, 그 외 경우, 반환 값이 nil이 된다
특정 타입(type)으로의 변환이 실패로 보증된 것 또는 성공으로 보증된 것이면, 컴파일 시 에러가 발생하게 된다
as! 연산자는 표현식 (expression) 을 특정 타입(type)으로 강제 변환한다
as! 연산자는 옵셔널 타입이 아닌 특정 타입(type) 값을 반환한다
변환을 실패하면 런타임 에러가 발생하게 된다
ex. x as! T 는 (x as? T)! 와 똑같이 동작한다
타입 변환에 대한 더 많은 정보 및 타입-변환 연산자의 사용 예제는, Type Casting 페이지에서 확인할 수 있다
으뜸 표현식(Primary Expressions)
으뜸 표현식 (primary expressions) 은 가장 기초적인 종류의 표현식이다
그 자체로 표현식으로 사용할 수도 있고,
다른 낱말과 조합하여 접두사 표현식과, 중위 표현식, 및 접미사 표현식을 만들 수도 있다
1) 글자값 표현식(Literal Expression)
글자값 표현식(literal expression)은 문자열이나 수치 값 같은 평범한 글자값이나
배열 및 딕셔너리 글자값, 플레이그라운드(playground) 글자값, 또는 다음의 특수 글자값 중 하나로 구성된다
#file의 문자열 값은 언어 버전에 따라 달라지므로 이전 #filePath 동작에서 새 #fileID로 마이그레이션할 수 있다
(스위프트 5.3 이전까지의 #file 은 그 파일의 전체 경로였는데, 앞으로는 파일과 모듈 이름으로 바뀌게 된다)
현재 버전의 스위프트에선, #file는 #filePath 값과 똑같다
미래 버전의 스위프트에선, #file는 #fileID 값과 똑같을 예정이다
미래의 동작을 채택하려면, #file 을 #fileID 나 #filePath 로 적절하게 교체하여 사용하면 된다
미래 버전의 스위프트에선 #file 과 #filePath 를 확실하게 구분할 예정이며
#filePath 를 출하용 프로그램에서만 사용할 것을 권하는데, 이러한 구분은 개인 정보 보호(privacy) 정책과도 관련된다
#fileID 표현식의 문자열 값은 모듈/파일(module/file) 형식인데,
파일(file)은 표현식이 있는 파일 이름이고
모듈(module)은 이 파일이 속해 있는 모듈 이름이다
#filePath 표현식의 문자열 값은 표현식이 있는 파일의 전체 파일-시스템 경로다
Line Control Statement 페이지에서 설명한 것처럼,
#fileID, #filePath 두 값 모두 #sourceLocation으로 바꿀 수 있다
#filePath와 달리, #fileID에는 소스 파일의 전체 경로를 포함하지 않기 때문에,
개인 정보를 더 잘 보호하며 컴파일한 바이너리의 크기가 줄여든다
테스트, 빌드 스크립트 또는 그 외 출하용 프로그램의 일부분이 아닌 코드 밖에선 #filePath의 사용을 피해야 한다
개인이 소유하거나 컴파일하여 바이너리로 변환할 게 아닌 코드엔 #filePath 를 사용하지 않음으로써 개인 정보를 보호할 수 있다
#function 값은
함수 안에선 해당 함수의 이름,
메소드 안에선 해당 메소드의 이름,
프로퍼티의 게터나 세터 안에선 해당 프로퍼티의 이름,
init 이나 subscript 같은 특수 멤버 안에선 해당 키워드의 이름,
파일의 최상단에선 현재 모듈의 이름이다
함수나 메소드 매개 변수의 기본 값으로 사용할 땐, 호출한 쪽에서 기본 값 표현식을 평가할 때 특수 글자값의 값을 결정한다
배열 글자값(array literal)은 순서 있는 값의 집합체(ordered collection)다
형식은 다음과 같다
배열의 마지막 표현식 뒤에 쉼표가 있어도 된다
배열 글자값은 [T] 타입인데, 여기서 T는 그 안의 표현식 타입이다
표현식의 타입이 여러 개면, T는 이들의 가장 가까운 공통 상위 타입(closest common supertype)이 된다
빈 배열 글자값은 빈 대괄호 쌍으로 작성하며, 이를 사용하여 특정 타입의 빈 배열을 생성할 수 있다
딕셔너리 글자값(dictionary literal)은 순서 없는 키-값(key-value)쌍의 집합체(unordered collection)다
형식은 다음과 같다
딕셔너리의 마지막 표현식 뒤에 쉼표가 있어도 된다
딕셔너리 글자값은 [Key : Value] 타입인데,
여기서 Key는 키 표현식의 타입이고
Value는 값 표현식의 타입이다
표현식의 타입이 여러 개면, Key와 Value 는 각자 값의 가장 가까운 공통 상위 타입이 된다
빈 딕셔너리 글자값은 빈 배열 글자값과 구별하려고 대괄호 쌍 안에 콜론([:])을 작성한다
빈 딕셔너리 글자 값을 사용하여 특정 키와 값 타입의 빈 딕셔너리 글자 값을 생성할 수 있다
플레이그라운드 글자값(playground literal)은 엑스코드(Xcode) 에서 사용하는 것으로
프로그램 편집기 안에서 색상, 파일 또는 이미지를 대화형으로 나타내도록 한다
엑스코드 밖에 있는 평범한 텍스트에선 플레이그라운드 글자값을 특수 글자 값 구문으로 나타낸다
ex. ‘빨간색’ 플레이그라운드 글자 값은 인데,
이를 복사하여 다른 편집기로 옮기면
var color = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 1)과 같은 특수 글자값 구문을 써서 나타낸다
엑스코드에서 플레이그라운드 글자 값을 사용하는 정보는,
엑스코드 도움말 (Xcode Help) 의 Add a color, file, or image literal 페이지에서 더 알 수 있다
2) Self 표현식
self 표현식은 현재 타입 또는 자기가 있는 타입의 인스턴스를 명시적으로 참조한다
형식은 다음과 같다
이니셜라이저, 서브 스크립트 연산, 또는 인스턴스 메소드 안에서의 self는
자기가 있는 타입의 현재 인스턴스를 참조한다
타입 메소드 안에서의 self 는 자기가 있는 현재 타입을 참조한다
멤버에 접근할 때 self 표현식으로 영역을 지정하면,
함수 매개 변수 같이 영역에 동일한 이름의 다른 변수가 있을 때도 헷갈리지 않는다
예를 들면 다음과 같다
값 타입의 mutating 메소드 안에선,
해당 값 타입의 새로운 인스턴스를 self 에 할당할 수 있다
예를 들면 다음과 같다
3) 상위 클래스 표현식(Superclass Expression)
상위 클래스 표현식(superclass expression)은 클래스를 자신의 상위 클래스와 상호 작용하게 해준다
형식은 다음 중 하나다
첫 번째 형식으로는 상위 클래스의 멤버에 접근한다
두 번째 형식으로 상위 클래스의 서브 스크립트 구현에 접근한다
세 번째 형식으로 상위 클래스의 이니셜라이저에 접근한다
하위 클래스의 멤버와, 서브 스크립트 및 이니셜라이저 구현 안에
상위 클래스 표현식을 사용하면 자신의 상위 클래스 안의 구현을 사용할 수 있다
4) 클로저 표현식
클로저 표현식(closure expression)은,
다른 프로그래밍 언어에선 람다(lambda)나 익명 함수(anonymous function)라고도 하는클로저를 생성한다
함수 선언처럼 클로저도 구문을 담으며
자신을 둘러싼 영역의 상수와 변수를 붙잡게(capturoing) 된다
형식은 다음과 같다
매개 변수 (parameter)의 형식은 Function Declaration 페이지에서 설명한 것 처럼
함수 선언의 매개 변수와 동일하다
클로저 표현식에 throws나 async를 써서 클로저가 에러 핸들링을 하거나 비동기라는 걸 명시한다
클로저 본문이 try 표현식을 포함하면, 던지는 클로저라고 이해한다
마찬가지로, await 표현식을 포함하면, 비동기라고 이해한다
클로저를 더 간결하게 작성하는 여러가지 특수한 형식이 있다
매개 변수 이름과 두 타입 모두를 생략하면, 구문 앞의 in 키워드도 생략한다
생략한 타입을 추론할 수 없으면, 컴파일 에러가 발생한다
그러면 매개 변수가 $ 뒤에 자신의 위치가 붙은 암시적 이름을 가지게 된다
$0, $1, $2, 등으로 계속된다
주위의 표현식 타입을 추론할 땐 해당 표현식의 내용도 고려한다
다음의 클로저는 모두 동일하다
클로저를 함수 인자로 전달하는 정보는, Function Call Expression 페이지에서 더 알 수 있다
함수 호출 부분에서 곧바로 클로저를 사용할 때 처럼, 변수나 상수에 저장하지 않고도 클로저를 사용할 수 있다
위 코드에서 myFunction에 전달한 클로저 표현식이 곧바로 사용하는 예시다
그 결과, 클로저 표현식이 벗어나는 건지 벗어나지 않는 건지는 표현식의 주위 상황에 달려 있게 된다
표현식 자체가 아니라 표현식을 호출하는 쪽에 달려 있다
곧바로 호출하거나 벗어나지 않는 함수 인수로 전달한 클로저 표현식은 벗어나지 않게 된다
그 외 경우의 클로저 표현식은 벗어나게 된다
벗어나는 클로저의 더 많은 정보는 Escaping Closures 페이지에서 알 수 있다
5) 캡쳐 리스트
기본적으로, 클로저 표현식은 자기 주위 영역의 상수와 변수 값을 강한 참조로 붙잡는다
캡쳐 리스트를 사용하면 클로저에서 값을 붙잡는 방법을 명시적으로 제어할 수 있다
캡쳐 리스트는 매개 변수 목록 앞에 쉼표로 구분한 표현식 목록을 대괄호로 둘러싸서 작성한다
캡쳐 리스트를 사용하면 매개 변수 이름, 매개 변수 타입 및 반환 타입을 생략한 경우라도 반드시 in 키워드를 사용해야 한다
캡쳐 리스트 안의 요소는 클로저 생성 때 초기화한다
붙잡을 목록의 각 요소마다, 한 상수를 주위 영역 안의 동일 이름인 상수나 변수 값으로 초기화한다
아래 코드 예제에서, a는 캡쳐 리스트에 포함하지만 b는 아니어서 서로 다르게 동작하게 된다
a라는 이름은 주위 영역 안의 변수와 클로저 영역 안의 상수라는 서로 다른 두 개가 있지만,
b라는 이름의 변수는 하나뿐이다
안쪽 영역의 a 는 클로저 생성 할당 때 바깥 영역의 a 값으로 초기화하지만,
이러한 값 타입은 어떤 특수한 방식으로도 연결되어 참조 된 것이 아니다
이는 바깥 영역의 a 값을 바꿔도 안쪽 영역의 a 값에 영향을 주지 않으며,
클로저 안의 a 를 바꿔도 클로저 밖의 a 값에 영향을 주지 않는다
이와 대조적으로, b 라는 이름의 변수는 바깥 영역의 b 단 하나만 있어서
클로저 안팎에서 할당에 따라서 바뀌어지는 걸 양쪽 다 볼 수 있다
캡쳐된 변수의 타입이 참조 의미 구조를 가질 땐 이런 구별이 보이지 않는다
예를 들어, 아래 코드에는 바깥 영역의 변수와 안쪽 영역의 두 개의 상수 x 가 있지만,
참조 의미 구조이기 때문에 둘 다 동일한 객체를 참조하게 된다
표현식 값의 타입이 클래스면 붙잡을 목록 안의 표현식에 weak 나 unowned 를 표시하여
표현식 값에 대한 약한 또는 소유하지 않는 참조를 붙잡을 수 있다
캡쳐 리스트에서 임의의 표현식과 이름 붙인 변수를 연결할 수도 있다
클로저 생성 때 표현식을 평가하여, 지정한 강도로 값을 붙잡는다
예를 들면 다음과 같다
클로저 표현식에 대한 더 많은 정보와 예제는 Closure Expressions 페이지에서 확인할 수 있다
캡쳐 리스트에 대한 더 많은 정보와 예제는 Resolving Strong Reference Cycles for Closures 페이지에서 확인할 수 있다
6) 암시적 멤버 표현식(Implicit Member Expression)
암시적 멤버 표현식(implicit member expression)은 열거체 case나 타입 메소드 같이,
암시적 타입을 추론할 수 있는 상황에서 타입 멤버에 접근하는 단축 방식이다
형식은 다음과 같다
추론한 타입이 옵셔널이면, 암시적 멤버 표현식 안에서 옵셔널 아닌 타입의 멤버도 사용할 수 있다
암시적 멤버 표현식 뒤엔 접미사 표현식에 나열된 접미사 연산자나 다른 접미사 구문이 있을 수 있다
이를 연쇄된 암시적 멤버 표현식(chained implicit member expression)이라고 한다
연쇄한 모든 접미사 표현식이 동일 타입을 가지는 게 일반적이지만,
유일한 요구 사항은 연결된 암시적 멤버 표현식 전체를 컨텍스트에서 암시하는 형식으로 변환할 수 있어야 한다는 것이다
특히, 암시 타입이 옵셔널이면 옵셔널 아닌 타입의 값을 사용할 수도 있고,
암시 타입이 클래스 타입이면 자신의 하위 클래스 타입 중 하나의 값을 사용할 수도 있다
예를 들면 다음과 같다
위 코드에서,
x 의 타입은 자신의 암시 타입과 정확하게 일치하고,
y 의 타입은 SomeClass 에서 SomeClass? 로 변환 가능하며,
z 의 타입은 SomeSubclass 에서 SomeClass 로 변환 가능하다
7) 괄호 표현식(Parenthesized Expression)
괄호 표현식(parenthesized expression)은 표현식을 괄호로 둘러싸서 구성한다
괄호를 사용하여 표현식을 명시적으로 그룹지으면
연산의 우선 순위를 지정할 수 있다
괄호 그룹짓기는 표현식의 타입을 바꾸지 않는데, 예를 들어, (1)의 타입은 단순히 Int가 된다
8) 튜플 표현식
튜플 표현식(tuple expression)은 쉼표로 구분한 표현식 목록을 괄호로 둘러싸서 구성한다
각각의 표현식엔 옵션으로 자기 앞에 콜론(:)으로 구분한 식별자가 있을 수 있다
형식은 다음과 같다
튜플 표현식에 있는 각각의 식별자는
튜플 표현식 영역 안에서 반드시 유일해야 한다
중첩 튜플 표현식에선 동일 중첩 수준의 식별자는 반드시 유일해야 한다
예를 들어, (a: 10, a: 20) 은 무효한데 동일한 수준에 이름표 a가 두 번 나타나기 때문이다
하지만, (a: 10, b: (a: 1, x: 2)) 는 유효하다
a가 두 번 나타나긴 하지만, 한 번은 바깥쪽 튜플에서 한 번은 안쪽 튜플에서 나타난다
튜플 표현식은 0개의 표현식을 담거나,
둘 이상의 표현식을 담을 수 있다
괄호 안에 표현식이 단 하나면 괄호 표현식으로 인식한다
8) 와일드카드 표현식
와일드카드 표현식(wildcard expression)을 사용하면 할당 중에 값을 명시적으로 무시한다
예를 들어, 다음 할당에선 10은 x에 할당하고 20은 무시된다
9) 키-경로 표현식(Key-Path Expression)
키-경로 표현식(key-path expression)은 타입의 프로퍼티이나 서브 스크립트를 참조한다
키-값 관찰 같은 동적 프로그래밍 임무에서 키-경로 표현식을 사용하는데
형식은 다음과 같다
타입 이름(type name)은 String 이나 [Int], 또는 Set 같이
어떤 일반화 매개 변수도 포함한 고정 타입의 이름이다
경로(path)는 프로퍼티 이름과, 서브 스크립트, 옵셔널 체이닝 표현식 및 강제 언래핑 표현식으로 구성된다
이 각각의 키-경로 성분은 필요한 만큼 많이 어떤 순서로든 반복할 수 있다
컴파일 시점에 키-경로 표현식을 KeyPath 클래스의 인스턴스로 교체한다
키-경로로 값에 접근하려면 subscript(keyPath:) 서브 스크립트를 통해 키 경로를 전달하면 되는데,
이는 모든 타입에서 사용 가능하다
예를 들면 다음과 같다
타입 추론이 암시 타입을 결정할 수 있는 상황에선 타입 이름(type name)을 생략할 수 있다
다음 코드는 \SomeClass.someProperty 대신 \.someProperty 를 사용한다
경로(path)는 self를 참조하여 자기 식별 키 경로(\.self) 를 생성할 수 있다
자기 식별 키 경로는 인스턴스 전체를 참조하므로,
이를 사용하여 변수 안에 저장한 모든 데이터를 한 번만에 접근하고 바꿀 수 있다.
예를 들면 다음과 같다
경로(path)는 프로퍼티의 프로퍼티를 참조하기 위해 마침표로 구분된 여러 프로퍼티 이름이 포함될 수 있다
다음 코드는 \OuterStructure.outer.someValue 키 경로 표현식을 사용하여
OuterStructure 타입의 outer 프로퍼티에 있는 someValue 프로퍼티에 접근한다
경로(path)는 서브 스크립트의 매개 변수 타입이 Hashable 프로토콜을 준수하면
대괄호를 써서 서브 스크립트를 포함할 수 있다
다음 예제는 키 경로에서 서브 스크립트를 써서 배열의 두 번째 원소에 접근한다
서브 스크립트에서 사용할 수 있는 값은 이름 붙인 값 또는 글자값이다
키 경로는 값 의미 구조로 값을 캡쳐한다
다음 코드는 키-경로 표현식과 클로저 둘 다에서
index 변수로 greetings 배열의 세 번째 원소에 접근한다
index가 수정되면, 키-경로 표현식이 여전히 세 번째 원소를 참조하는 동안 클로저는 새롭게 수정된 index를 사용한다
경로(path)는 옵셔널 체이닝과 강제 언래핑을 사용할 수 있다
다음 코드는 키 경로에서 옵셔널 체이닝을 써서 옵셔널 문자열 안의 프로퍼티에 접근한다
키 경로의 성분을 섞어서 맞춰보면 타입 안에 깊숙히 중첩된 값에 접근할 수 있다
다음 코드는 이러한 성분을 조합한 키-경로 표현식을 사용하여
배열 딕셔너리의 서로 다른 값과 프로퍼티에 접근한다
보통은 함수나 클로저를 제공할 상황에서 키 경로 표현식을 사용할 수 있다
특히, (SomeType) -> Value 타입의 함수나 클로저 대신,
근원 타입은 SomeType이고 경로는 Value 타입의 값을 만드는 키 경로 표현식을 사용할 수도 있다
키 경로 표현식의 어떤 사이드 이펙트든 표현식을 평가하는 시점에만 평가한다
예를 들어, 키 경로 표현식의 서브 스크립트 안에 함수 호출을 만들면
표현식 평가 부분에서 단 한번만 함수를 호출할 뿐, 키 경로를 사용할 때마다 호출하진 않는다
오브젝티브-C API 와 상호 작용하는 코드에서의
키 경로 사용에 대한 더 많은 정보는,
Using Objective-C Runtime Features in Swift 페이지에서 더 알 수 있다
키-값 코딩과 키-값 관찰에 대한 정보는,
Key-Value Coding Programming Guide 페이지와
Key-Value Observing Programming Guide 페이지에서 더 알 수 있다
10) 셀렉터 표현식(Selector Expression)
셀렉터 표현식은 셀렉터에 접근해서 오브젝티브-C 의 메소드나 프로퍼티의 게터 또는 세터를 참조하게 해준다
형식은 다음과 같다
메소드 이름(method name)과 프로퍼티 이름(property name)은
반드시 오브젝티드-C 런타임에서 사용 가능한 메소드나 프로퍼티를 참조해야 한다
셀렉터 표현식의 값은 Selector 타입의 인스턴스가 된다
예를 들면 다음과 같다
프로퍼티의 게터에 대한 셀렉터를 만들 때
프로퍼티 이름은 변수 또는 상수 프로퍼티에 대한 참조일 수 있다
반대로, 프로퍼티의 세터에 대한 셀렉터를 만들 때
프로퍼티 이름은 반드시 변수 프로퍼티에 대한 참조여야 한다
메소드 이름(method name)은 이름은 공유하지만 타입 서명은 다른 메소드 사이의 모호함을 없애는 as 연산자뿐 아니라,
그룹을 짓기 위한 괄호도 담을 수 있다
타입 서명이 다르다는 건
중복 정의(overloading)하여 함수 이름은 같지만, 매개 변수 및 반환 타입을 포함한 함수의 타입 자체는 다른 메소드들 의미한다
예를 들면 다음과 같다
런타임이 아닌 컴파일 시점에 셀렉터를 생성하기 때문에,
컴파일러는 메소드 및 프로퍼티가 존재하는지 그리고 오브젝티브-C 런타임에 노출되는지 확인할 수 있다
오브젝티브-C API와 상호 작용하는 스위프트 코드에서의 셀렉터 사용에 대한 더 많은 정보는
Using Objective-C Runtime Features in Swift 페이지에서 확인할 수 있다
11) 키 경로 문자열 표현식(Key-Path String Expression)
키-경로 문자열 표현식은 문자열에 접근해서,
키-값 코딩과 키-값 관찰 API 에서 사용할, 오브젝티브-C의 프로퍼티를 참조하게 해준다
‘키-경로 문자열 표현식(key-path string expression)’ 은
‘키-경로 표현식(key-path expression)’ 을 오브젝티브-C 의 프로퍼티에 사용하기 위한 방법이다
프로퍼티 이름(property name)은 반드시 오브젝티브-C 런타임에서 사용 가능한 프로퍼티를 참조해야 한다
컴파일 시점에 키-경로 문자열 표현식을 문자열 글자값으로 교체한다
예를 들면 다음과 같다
클래스 안에서 키-경로 문자열 표현식을 사용할 땐,
클래스 이름 없이 그냥 프로퍼티 이름만 작성하여, 해당 클래스의 프로퍼티를 참조할 수 있다
런타임이 아닌 컴파일 시점에 키 경로 문자열을 생성하기 때문에,
컴파일러는 프로퍼티가 존재하는지 그리고 오브젝티브-C 런타임에 노출되는지 확인할 수 있다
오브젝티브-C API 와 상호 작용하는 스위프트 코드에서의 키 경로 사용에 대한 더 많은 정보는,
Using Objective-C Runtime Features in Swift 페이지에서 확인할 수 있다
키-값 코딩과 키-값 관찰에 대한 정보는,
Key-Value Coding Programming Guide 항목과
Key-Value Observing Programming Guide 항목 페이지에서 확인할 수 있다
접미사 표현식(postfix expressions)
접미사 표현식(postfix expressions)은 접미사 연산자나 그 외 접미사 구문을 표현식에 적용함으로써 형성한다
구문상 모든 으뜸 표현식 또한 접미사 표현식에 포함된다
이러한 연산자의 동작에 대한 정보는,
Basic Operators와 Advanced Operators 페이지에서 확인할 수 있다
스위프트 표준 라이브러리가 제공하는 연산자에 대한 정보는,
Operator Declarations 페이지에서 확인할 수 있다
1) 함수 호출 표현식
함수 호출 표현식(function call expression)은 함수 이름과
그 뒤의 소괄호 안에 있는 쉼표로 구분한 함수 매개 변수 목록으로 구성된다
함수 호출 표현식의 형식은 다음과 같다
함수 이름(function name)은 그 값이 함수 타입인 어떤 표현식이든 가능하다
함수 정의가 자신의 인수 라벨을 포함하면,
함수 호출도 반드시 자신의 인자 값 앞에 콜론(:)으로 구분한 이름을 포함해야 한다
이런 종류의 함수 호출 표현식 형식은 다음과 같다
함수 호출 표현식은 닫는 괄호 바로 뒤에 클로저 표현식 형식의 후행 클로저(trailing closures)를 포함할 수 있다
후행 클로저는 괄호 안 마지막 매개 변수 뒤에 추가된 함수 매개 변수로 여겨진다
첫 번째 클로저 표현식엔 이름표를 붙이지 않으며
추가적인 어떤 클로저 표현식이든 앞에 자신의 인수 라벨을 붙인다
아래 예제는 후행 클로저 구문을 사용한 것과 하지 않은 서로 같은 함수 호출을 보여준다
후행 클로저가 함수의 유일한 인자면, 소괄호를 생략할 수 있다
후행 클로저를 매개 변수에 포함하려고, 다음처럼 컴파일러가 함수 매개 변수를 왼쪽에서 오른쪽으로 검토한다
후행 클로저는 자신과 일치한 매개 변수에 인수로 전달된다
검색 프로세스 중에 건너 뛴 매개 변수엔 인수를 전달하지 않는다
예를 들어, 기본 매개 변수를 사용할 수 있는 경우가 있다
일치한 걸 찾은 후에 그 다음 후행 클로저 및 그 다음 매개 변수를 계속 검색한다
일치 프로세스가 끝나면 반드시 모든 후행 클로저에 일치하는 항목이 있어야 한다
매개 변수가 In-Out 매개 변수가 아니면서,
다음 중 하나라면 매개 변수가 함수 타입과 구조가 닮은(structually resembles)거로 여긴다
후행 클로저가 함수 타입과 타입이 닮은 구조인 매개 변수와 일치하나,
함수가 아닐 땐, 필요에 따라 클로저가 래핑된다
예를 들어, 매개 변수 타입이 옵셔널 타입이면 자동으로 클로저를 Optional로 포장한다
이런 맞춤을 오른쪽에서 왼쪽으로 실시했던 스위프트 5.3 이전 버전의 코드를 쉽게 마이그레이션하도록,
컴파일러는 왼쪽에서 오른쪽과 오른쪽에서 왼쪽 순서 둘 다 검사한다
검사한 방향에 따라 결과가 다르게 생성되면,
예전의 오른쪽에서 왼쪽 순서를 사용하고 컴파일러가 경고를 생성한다
미래 버전의 스위프트는 항상 왼쪽에서 오른쪽 순서를 사용할 예정이다
위 예제에서, “ambiguous” 으로 표시한 함수 호출은 “- 120” 을 인쇄하며 스위프트 5.3에서 컴파일러 경고를 표시한다
미래 버전의 스위프트는 “110 -“ 을 인쇄하게 된다
클래스나, 구조체, 또는 열거체 타입은
Methods with Special Names에서 설명한
여러가지 메소드 중 하나를 선언함으로써
수월한 함수 호출 구문을 사용할 수 있다
2) 포인터 타입으로의 암시적 변환(Implicit Conversion to a Pointer Type)
함수 호출 표현식에서 인수와 매개 변수 타입이 서로 다르면,
컴파일러는 이들의 타입을 맞춰보려고 다음 목록에 있는 암시적 변환 중 하나를 적용한다
다음의 두 함수 호출은 서로 동일하다
이 암시적 변환들로 생성한 포인터는 함수 호출 지속 시간에만 유효하다
정의되지 않은 동작을 피하려면, 함수 호출이 끝난 후 절대로 코드가 포인터를 물고 있지 않도록 보장해야 한다
withUnsafePointer(to:) 같은 명시적 함수 대신 &를 사용하야 In-Out 매개 변수처럼 활용하는 건,
특히 함수가 여러가지 포인터 인수를 취할 때의, Low 레벨 C 함수 호출을 더 읽기 쉽게 만들 수 있다
하지만, 다른 스위프트 코드의 함수를 호출할 땐 안전하지 않은 API를 명시적으로 사용하는 대신 &를 사용하는 걸 피하도록 해야한다
3) 이니셜라이저 표현식
이니셜라이저 표현식(initializer expression)은 타입 이니셜라이저에 대한 접근을 제공한다
형식은 다음과 같다
함수 호출 표현식에서 이니셜라이저 표현식을 사용하여 타입의 새로운 인스턴스를 초기화한다
이니셜라이저 표현식을 사용하여 상위 클래스 이니셜라이저로 일을 위임하기(delegate)도 한다
함수 같이 이니셜라이저를 값처럼 사용할 수도 있다
예를 들면 다음과 같다
타입 이름을 정하면 이니셜라이저 표현식을 사용하지 않고 타입 초기자에 접근할 수 있다
다른 모든 경우엔, 반드시 이니셜라이저 표현식을 사용해야 한다
4) 명시적 멤버 표현식(Explicit Member Expression)
명시적 멤버 표현식(explicit member expression)은 이름 붙인 타입이나, 튜플, 또는 모듈의 멤버에 대한 접근을 허용한다
이는 항목과 자신의 멤버 식별자 사이의 마침표(.)로 구성된다
이름 붙인 타입의 멤버는 타입의 선언이나 익스텐션 부분에서 이름이 붙는다
예를 들면 다음과 같다
튜플의 멤버는 암시적으로,
자신이 나타나는 순서대로 0 부터 시작하는 정수로 이름이 붙는다
예를 들면 다음과 같다
모듈의 멤버는 그 모듈의 최상단 선언들에 접근한다
dynamicMemberLookup 특성으로 선언한 타입은,
Attributes에서 설명한 것처럼,
런타임에 찾아 보는 멤버를 포함한다
함수 이름이 같고, 자신의 인수 이름만 다른 메소드나 이니셜라이저를 구별하려면,
괄호 안에 인수 이름을 포함하고, 각각의 인자 이름 뒤에 콜론(:)을 붙인다
이름 없는 인수엔 밑줄(_, 와일드카드)를 작성한다
중복 정의한 메소드를 구별하려면 타입 명시 주석을 사용한다
예를 들면 다음과 같다
마침표가 줄 맨 앞에 나타나면,
암시적 멤버 표현식이 아닌 명시적 멤버 표현식의 일부분으로 이해한다
예를 들어, 아래 나열한 건 연쇄적인 메소드 호출을 여러 줄로 쪼갠 걸 보여준다
이렇게 여러 줄로 연쇄한 구문을 컴파일러 제어문으로 조합하여
각각의 메소드 호출 시점을 제어할 수 있다
예를 들어, 다음 코드는 iOS 에선 다른 규칙으로 걸러낸다
#if 와 #endif 및 다른 컴파일 지시자 사이에 있는 조건부 컴파일 블럭은
접미사 표현식을 만들기 위해 0개 이상의 접미사 뒤에 암시적 멤버 표현식을 포함할 수 있다
또 다른 조건부 컴파일 블럭이나, 이러한 표현식과 블럭들을 조합한 것도 담을 수 있다
해당 구문은 최상단 코드 뿐만 아니라, 명시적 멤버 표현식을 작성할 수 있는 어떤 곳이든 사용할 수 있다
조건부 컴파일 블럭에서 #if 컴파일 지시자 분기는 반드시 적어도 하나의 표현식을 담아야 한다
다른 분기는 비어도 관계없다
5) 접미사 self 표현식
접미사 self 표현식은 표현식 또는 타입 이름과 바로 뒤의 .self로 구성된다
형식은 다음과 같다
첫 번째 형식은 표현식(expression)의 값을 평가한다
ex. x.self 는 x를 평가한다
두 번째 형식은 타입(type)의 값을 평가한다
타입을 값으로 접근하려면 해당 형식을 사용한다
ex. SomeClass.self 는 SomeClass 타입 그 자체라고 평가하기 때문에,
타입-수준 매개 변수를 받아들이는 함수나 메소드에 전달할 수 있다
6) 서브 스크립트 표현식
서브 스크립트 표현식(subscript expression)은 해당 서브스크립트 선언의 게터와 세터를 사용한 서브 스크립트 접근을 제공한다
형식은 다음과 같다
서브 스크립트 표현식의 값을 평가하려면
서브 스크립트 매개 변수로 전달한 인덱스 표현식(index expressions)으로
표현식(expression) 타입의 서브 스크립트 게터를 호출한다
값을 설정하려면, 서브 스크립트 세터를 똑같은 방식으로 호출한다
서브 스크립트 선언에 대한 정보는,
Protocol Subscript Declaration 페이지에서 확인할 수 있다
7) 강제 해제 값 표현식(Forced-Value Expression)
강제 해제 값 표현식(forced-value expression)은 nil이 아닌 게 확실한 옵셔널 값의 래핑을 풀어낸다
형식은 다음과 같다
표현식(expression)값이 nil 이 아니면, 옵셔널 값의 포장을 풀고 옵셔널-아닌 해당 타입으로 반환된다
그 외 경우, 런타임 에러가 발생한다
강제 해제 값 표현식의 언래핑된 값은
값 그 자체를 변경하거나
값의 멤버 중 하나에 할당함으로써 수정할 수 있다
예를 들면 다음과 같다
8) 옵셔널 체이닝 표현식(Optional-Chaining Expression)
옵셔널 체이닝 표현식(optional-chaining expression)은
접미사 표현식에서 옵셔널 값을 사용하기 위한 단순화 구문을 제공한다
형식은 다음과 같다
접미사 ? 연산자는 표현식 값을 바꾸지 않고도 표현식을 옵셔널 체이닝 표현식으로 만든다
옵셔널 체이닝 표현식은 반드시 접미사 표현식 안에 있어야 하며,
접미사 표현식을 특수한 방식으로 평가하게 한다
옵셔널 체이닝 표현식 값이 nil 이면,
접미사 표현식 안의 모든 다른 연산을 무시하고
전체 접미사 표현식을 nil 로 평가한다
옵셔널 체이닝 표현식 값이 nil 이 아니면,
옵셔널 체이닝 표현식 값의 포장을 풀고
이를 써서 접미사 표현식의 나머지 부분을 평가한다
어느 경우든, 접미사 표현식 값은 여전히 옵셔널 타입이다
옵셔널 체이닝 표현식을 담은 접미사 표현식이 다른 접미사 표현식 안에 중첩되어 있으면,
가장 바깥쪽 표현식만 옵셔널 타입을 반환한다 - 옵셔널에 옵셔널을 중첩하지는 않는다
아래 예제에서 c가 nil 이 아닐 때, 값의 포장을 풀고 이를 써서 .property 를 평가한 후
해당 값으로 .performAction() 을 평가한다
전체 표현식 c?.property.performAction() 의 값은 옵셔널 타입이게 된다
다음 예제는 위 예제 동작을 옵셔널 체이닝 사용없이 옵셔널 바인딩하는 걸 보여준다
옵셔널 체이닝 표현식의 언래핑한 값은
값 그 자체를 변경하거나 값의 멤버 중 하나에 할당함으로써 수정할 수 있다
옵셔널 체이닝 표현식 값이 nil 이면,
할당 연산자의 오른쪽 표현식을 평가하지 않는다
예를 들면 다음과 같다
The text was updated successfully, but these errors were encountered: