선언 (Declarations)

선언 (declaration) 은 프로그램에 새로운 이름 또는 구성을 도입합니다. 예를 들어 함수와 메서드를 도입하기 위해, 변수와 상수를 도입하기 위해, 그리고 열거형, 구조체, 클래스, 그리고 프로토콜 타입을 정의하기 위해 선언을 사용합니다. 기존에 명명된 타입의 동작을 확장하기 위해 그리고 다른 곳에서 선언된 기호를 프로그램으로 가져오기 위해 선언을 사용할 수도 있습니다.

Swift 에서 대부분의 선언은 선언과 동시에 구현되거나 초기화 된다는 의미에서도 정의이기도 합니다. 이 말은 프로토콜은 멤버를 정의하지 않으므로 대부분 프로토콜 멤버는 선언만 있습니다. 편의상 그리고 Swift 에서 구별이 그다지 중요하지 않으므로 선언 (declaration) 은 선언과 정의를 모두 포함합니다.

GRAMMAR OF A DECLARATION declaration → import-declaration declaration → constant-declaration declaration → variable-declaration declaration → typealias-declaration declaration → function-declaration declaration → enum-declaration declaration → struct-declaration declaration → class-declaration declaration → protocol-declaration declaration → initializer-declaration declaration → deinitializer-declaration declaration → extension-declaration declaration → subscript-declaration declaration → operator-declaration declaration → precedence-group-declaration declarations → declaration declarations opt_{opt}

최상위-수준 코드 (Top-Level Code)

Swift 소스 파일에서 최상위-수준 코드는 0 또는 그 이상의 구문, 선언, 그리고 표현식으로 구성됩니다. 기본적으로 소스 파일의 최상위-수준에 선언된 변수, 상수, 그리고 다른 명명된 선언은 동일한 모듈의 일부인 모든 소스 파일 코드에 접근할 수 있습니다. 접근 제어 수준 (Access Control Levels) 에서 설명된대로 접근-수준 수정자 (access-level modifier) 로 선언을 표시하여 기본 동작을 재정의할 수 있습니다.

최상위-수준 선언 (top-level declarations) 과 실행 가능한 최상위-수준 코드 (executable top-level code) 인 최상위-수준 코드의 두가지 종류가 있습니다. 최상위-수준 선언은 선언으로만 구성되고 모든 Swift 소스 파일에서 허용됩니다. 실행 가능한 최상위-수준 코드는 선언 뿐만 아니라 구문과 표현식을 포함하고 프로그램에 대해 최상위-수준 진입점으로만 허용됩니다.

실행 가능하게 만들기 위해 컴파일 한 Swift 코드는 코드가 파일과 모듈로 구성되는 방식과 상관없이 최상위-수준 진입점을 나타내는 다음 접근 방식 중 하나만 포함할 수 있습니다: main 속성, NSApplicationMain 속성, UIApplicationMain 속성, main.swift 파일, 또는 최상위-수준 실행 가능한 코드를 포함하는 파일.

GRAMMAR OF A TOP-LEVEL DECLARATION top-level-declaration → statements opt_{opt}

코드 블럭 (Code Blocks)

코드 블럭 (code block) 은 선언 및 제어 구조에서 구문을 그룹화 하기위해 사용됩니다. 다음의 형식을 가집니다:

코드 블럭 내에 구문 (statements) 은 선언, 표현식, 그리고 구문의 다른 종류가 포함되고 소스 코드에 나타나는 순서대로 실행됩니다.

GRAMMAR OF A CODE BLOCK code-block → { statements opt_{opt} }

가져오기 선언 (Import Declaration)

가져오기 선언 (import declaration) 을 사용하여 현재 파일 바깥에 선언된 기호에 접근할 수 있습니다. 기본 형식은 전체 모듈을 가져옵니다; import 키워드 다음에 모듈 이름이 따라오도록 구성됩니다:

가져올 기호에 대한 자세한 제한을 제공하면 모듈 또는 하위 모듈 내에 특정 하위 모듈 또는 특정 선언을 지정할 수 있습니다. 이런 상세한 형식이 사용되면 선언한 모듈이 아닌 가져온 기호만 현재 범위에서 사용할 수 있습니다.

GRAMMAR OF AN IMPORT DECLARATION import-declaration → attributes opt_{opt} import import-kind opt import-path import-kind → typealias | struct | class | enum | protocol | let | var | func import-path → import-path-identifier | import-path-identifier . import-path import-path-identifier → identifier | operator

상수 선언 (Constant Declaration)

상수 선언 (constant declaration) 은 프로그램에 명명된 상수값을 도입합니다. 상수 선언은 let 키워드를 사용하여 선언되고 다음의 형식을 가집니다:

상수 선언은 상수 이름 (constant name) 과 초기화 구문 표현식 (expression) 의 값 사이의 변경 불가능한 바인딩을 정의합니다; 상수의 값이 설정된 후에 변경할 수 없습니다. 이 말은 상수가 클래스 객체로 초기화되면 이 객체 자체는 변경할 수 있지만 이 상수 이름과 참조하는 객체 간의 바인딩은 변경할 수 없습니다.

상수가 전역 범위로 선언되면 값으로 반드시 초기화 되어야 합니다. 상수 선언이 함수 또는 메서드의 컨텍스트에 나타나면 해당 값을 처음 읽기 전에 값을 설정한다는 보장이 있는 한 나중에 초기화 될 수 있습니다. 컴파일러가 상수의 값을 읽지 않는다는 것을 증명할 수 있으면 상수에 값을 설정하지 않아도 됩니다. 상수 선언이 클래스 또는 구조체 선언의 컨텍스트에서 나타나면 상수 프로퍼티 (constant property) 로 간주됩니다. 상수 선언은 계산된 프로퍼티가 아니므로 getter 또는 setter 를 가지지 않습니다.

상수 선언의 상수 이름 (constant name) 이 튜플 패턴인 경우 튜플에서 각 항목의 이름은 초기화 구문 표현식 (expression) 에서 해당 값으로 바인딩 됩니다.

let (firstNumber, secondNumber) = (10, 42)

이 예제에서 firstNumber 는 값 10 에 대한 명명된 상수이고 secondNumber 는 값 42 에 대한 명명된 상수 입니다. 두 상수 모두 독립적으로 사용할 수 있습니다:

print("The first number is \(firstNumber).")
// Prints "The first number is 10."
print("The second number is \(secondNumber).")
// Prints "The second number is 42."

타입 주석 (: 타입) 은 타입 추론 (Type Inference) 에서 설명한대로 상수 이름 (constant name) 의 타입이 추론될 수 있을 때 상수 선언에서 선택사항입니다.

상수 타입 프로퍼티 (constant type property) 를 선언하려면 static 선언 수식어로 선언을 표시해야 합니다. 클래스의 상수 타입 프로퍼티는 암시적으로 final 입니다; class 또는 final 선언 수식어로 표시하여 하위클래스에 의한 재정의를 허용하거나 금지할 수 있습니다. 타입 프로퍼티는 타입 프로퍼티 (Type Properties) 에 설명되어 있습니다.

상수에 대한 자세한 내용과 사용시 지침에 대한 내용은 상수와 변수 (Constants and Variables)저장된 프로퍼티 (Stored Properties) 를 참고 바랍니다.

GRAMMAR OF A CONSTANT DECLARATION constant-declaration → attributes opt_{opt} declaration-modifiers opt_{opt} let pattern-initializer-list pattern-initializer-list → pattern-initializer | pattern-initializer , pattern-initializer-list pattern-initializer → pattern initializer opt_{opt} initializer → = expression

변수 선언 (Variable Declaration)

변수 선언 (variable declaration) 은 프로그램에 명명된 변수 값을 도입하고 var 키워드를 사용하여 선언됩니다.

변수 선언은 명명된, 변경 가능한 값, 저장된 그리고 계산된 변수와 프로퍼티, 저장된 변수와 프로퍼티 관찰자, 그리고 정적 변수 프로퍼티의 다양한 종류의 선언 형식을 가집니다. 사용할 적절한 형식은 변수가 선언되는 범위와 선언하려는 변수의 종류에 따라 다릅니다.

NOTE 프로토콜 프로퍼티 선언 (Protocol Property Declaration) 에서 설명한대로 프로토콜 선언의 컨텍스트에서 프로퍼티를 선언할 수도 있습니다.

재정의 (Overriding) 에서 설명한대로 override 선언 수식어로 하위클래스의 프로퍼티 선언을 표시하여 하위클래스에 프로퍼티를 재정의 할 수 있습니다.

저장된 변수와 저장된 변수 프로퍼티 (Stored Variables and Stored Variable Properties)

다음의 형식으로 저장된 변수 (stored variable) 또는 저장된 변수 프로퍼티 (stored variable property) 를 선언할 수 있습니다:

이러한 형식의 변수 선언은 전역 범위, 함수의 지역 범위, 또는 클래스나 구조체 선언의 컨텍스트에서 정의합니다. 이러한 형식의 변수 선언이 전역 범위나 함수의 지역 범위로 선언되면 저장된 변수 (stored variable) 로 참조됩니다. 클래스나 구조체 선언의 컨텍스트에서 선언되면 저장된 변수 프로퍼티 (stored variable property) 로 참조됩니다.

초기화 구문 표현식 (expression) 은 프로토콜 선언에 있을 수 없지만 다른 모든 컨텍스트에서 초기화 구문 표현식 (expression) 은 선택사항입니다. 즉, 초기화 구문 표현식 (expression) 이 없으면 변수 선언은 명시적 타입 주석 (: 타입) 을 포함해야 합니다.

상수 선언과 마찬가지로 변수 이름 (variable name) 이 튜플 패턴인 경우 튜플에서 각 항목에 이름은 초기화 구문 표현식 (expression) 에서 해당 값에 바인딩 됩니다.

이름에서 알 수 있듯이, 저장된 변수 또는 저장된 변수 프로퍼티의 값은 메모리에 저장됩니다.

계산된 변수와 계산된 프로퍼티 (Computed Variables and Computed Properties)

다음의 형식은 계산된 변수 (computed variable) 또는 계산된 프로퍼티 (computed property) 를 선언합니다:

이러한 형식의 변수 선언은 전역 범위, 함수의 지역 범위, 또는 클래스, 구조체, 열거형, 또는 확장 선언의 컨텍스트에서 정의합니다. 이러한 형식의 변수 선언을 전역 범위 또는 함수의 지역 범위로 선언되면 계산된 변수 (computed variable) 로 참조됩니다. 클래스, 구조체, 또는 확장 선언의 컨텍스트에서 선언되면 계산된 프로퍼티 (computed property) 로 참조됩니다.

getter 는 값을 읽기위해 사용되고 setter 는 값을 작성하기 위해 사용됩니다. setter 절은 선택사항이며 getter 만 필요하다면 읽기-전용 계산된 프로퍼티 (Read-Only Computed Properties) 에 설명한대로 getter 와 setter 절 모두 생략할 수 있고 간단하게 요구된 값을 직접적으로 반환할 수 있습니다. 그러나 setter 절을 제공하면 getter 절도 제공되어야 합니다.

setter 이름 (setter name) 과 둘러싸인 소괄호는 선택사항입니다. setter 이름을 제공하면 setter 에 파라미터의 이름으로 사용됩니다. setter 이름을 제공하지 않으면 짧은 Setter 선언 (Shorthand Setter Declaration) 에서 설명한대로 setter 에 기본 파라미터 이름은 newValue 입니다.

저장된 명명된 값과 저장된 변수 프로퍼티와 다르게 계산된 명명된 값 또는 계산된 프로퍼티의 값은 메모리에 저장되지 않습니다.

계산된 프로퍼티의 자세한 내용과 예제는 계산된 프로퍼티 (Computed Properties) 를 참고 바랍니다.

저장된 변수 관찰자와 프로퍼티 관찰자 (Stored Variable Observers and Property Observers)

willSetdidSet 관찰자를 저장된 변수 또는 프로퍼티에 선언할 수도 있습니다. 괄찰자와 함께 선언한 저장된 변수 또는 프로퍼티는 다음의 형식을 가집니다:

전역 범위, 함수의 지역 범위, 또는 클래스나 구조체 선언의 컨텍스트에서 이 형식의 변수 선언으로 정의합니다. 이 형식의 변수 선언이 전역 범위 또는 함수의 지역 범위로 선언되면 관찰자는 저장된 변수 관찰자 (stored variable observers) 로 참조됩니다. 클래스나 구조체 선언의 컨텍스트에서 선언되면 관찰자는 프로퍼티 관찰자 (property observers) 로 참조됩니다.

모든 저장된 프로퍼티에 프로퍼티 관찰자를 추가할 수 있습니다. 프로퍼티 관찰자 재정의 (Overriding Property Observers) 에서 설명한대로 하위클래스 내에 프로퍼티 재정의로 저장된 또는 계산된 프로퍼티와 상관없이 모든 상속된 프로퍼티에 프로퍼티 관찰자를 추가할 수도 있습니다.

초기화 구문 표현식 (expression) 은 클래스나 구조체 선언의 컨텍스트에서 선택사항 이지만 다른곳에선 필수입니다. 타입이 초기화 구문 표현식 (expression) 에서 유추될 수 있으면 타입 (type) 주석은 선택사항 입니다. 이 표현식은 프로퍼티의 값을 처음 읽을 때 평가됩니다. 프로퍼티의 초기값을 읽지않고 재작성하면 이 표현식은 프로퍼티에 처음 쓰기 전에 평가됩니다.

변수 또는 프로퍼티의 값이 설정되려고 하면 willSetdidSet 관찰자는 관찰 및 적절한 반응을 위한 방법을 제공합니다. 관찰자는 변수 또는 프로퍼티가 처음 초기화될 때는 호출되지 않습니다. 대신에 값이 초기화 컨텍스트 바깥에서 설정될 때만 호출됩니다.

willSet 관찰자는 변수 또는 프로퍼티의 값이 설정되기 전에만 호출됩니다. 새로운 값은 상수로 willSet 관찰자로 전달되고 willSet 절의 구현에서 변경할 수 없습니다. didSet 관찰자는 새로운 값이 설정된 후 바로 호출됩니다. willSet 관찰자와 달리 변수 또는 프로퍼티의 이전 값은 여전히 접근이 필요한 경우에 didSet 관찰자로 전달됩니다. 이 말은 didSet 관찰자 절 내에 변수 또는 프로퍼티에 값을 할당하면 할당한 새 값이 방금 설정되어 willSet 관찰자에 전달된 값을 대체합니다.

willSetdidSet 절에 setter 이름 (setter name) 과 둘러싸인 소괄호는 선택사항 입니다. setter 이름을 제공한다면 willSetdidSet 관찰자의 파라미터 이름으로 사용됩니다. setter 이름을 제공하지 않는다면 willSet 관찰자에서 기본 파라미터 이름은 newValue 이고 didSet 관찰자에서 기본 파라미터 이름은 oldValue 입니다.

willSet 절을 제공하면 didSet 절은 선택사항 입니다. 마찬가지로 willSet 절은 didSet 절이 제공되면 선택사항 입니다.

didSet 관찰자의 바디는 이전 값을 참조하면 getter 는 이전 값이 사용 가능하도록 관찰자 전에 호출됩니다. 아래 예제는 상위클래스에 의해 정의되고 관찰자를 추가하기 위해 하위클래스에 의해 재정의 된 계산된 프로퍼티를 보여줍니다.

class Superclass {
private var xValue = 12
var x: Int {
get { print("Getter was called"); return xValue }
set { print("Setter was called"); xValue = newValue }
}
}
// This subclass doesn't refer to oldValue in its observer, so the
// superclass's getter is called only once to print the value.
class New: Superclass {
override var x: Int {
didSet { print("New value \(x)") }
}
}
let new = New()
new.x = 100
// Prints "Setter was called"
// Prints "Getter was called"
// Prints "New value 100"
// This subclass refers to oldValue in its observer, so the superclass's
// getter is called once before the setter, and again to print the value.
class NewAndOld: Superclass {
override var x: Int {
didSet { print("Old value \(oldValue) - new value \(x)") }
}
}
let newAndOld = NewAndOld()
newAndOld.x = 200
// Prints "Getter was called"
// Prints "Setter was called"
// Prints "Getter was called"
// Prints "Old value 12 - new value 200"

프로퍼티 관찰자를 사용하는 방법에 대한 자세한 내용과 예제는 프로퍼티 관찰자 (Property Observers) 를 참고 바랍니다.

타입 변수 프로퍼티 (Type Variable Properties)

타입 변수 프로퍼티 (type variable property) 를 선언하기 위해 static 선언 수식어와 함께 선언에 표시합니다. 클래스는 상위클래스의 구현을 하위클래스가 재정의 할 수 있도록 class 선언 수식어와 함께 타입 계산된 프로퍼티를 표시할 수 있습니다. 타입 프로퍼티는 타입 프로퍼티 (Type Properties) 에 설명되어 있습니다.

GRAMMAR OF A VARIABLE DECLARATION variable-declaration → variable-declaration-head pattern-initializer-list variable-declaration → variable-declaration-head variable-name type-annotation code-block variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-block variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-keyword-block variable-declaration → variable-declaration-head variable-name initializer willSet-didSet-block variable-declaration → variable-declaration-head variable-name type-annotation initializer opt_{opt} willSet-didSet-block variable-declaration-head → attributes opt_{opt} declaration-modifiers opt_{opt} var variable-name → identifier getter-setter-block → code-block getter-setter-block → { getter-clause setter-clause opt_{opt} } getter-setter-block → { setter-clause getter-clause } getter-clause → attributes opt_{opt} mutation-modifier opt_{opt} get code-block setter-clause → attributes opt_{opt} mutation-modifier opt_{opt} set setter-name opt_{opt} code-block setter-name → ( identifier ) getter-setter-keyword-block → { getter-keyword-clause setter-keyword-clause opt_{opt} } getter-setter-keyword-block → { setter-keyword-clause getter-keyword-clause } getter-keyword-clause → attributes opt_{opt} mutation-modifier opt_{opt} get setter-keyword-clause → attributes opt_{opt} mutation-modifier opt_{opt} set willSet-didSet-block → { willSet-clause didSet-clause opt_{opt} } willSet-didSet-block → { didSet-clause willSet-clause opt_{opt} } willSet-clause → attributes opt_{opt} willSet setter-name opt_{opt} code-block didSet-clause → attributes opt_{opt} didSet setter-name opt_{opt} code-block

타입 별칭 선언 (Type Alias Declaration)

타입 별칭 선언 (type alias declaration) 은 프로그램에 기존 타입 명명된 별칭을 도입합니다. 타입 별칭 선언은 typealias 키워드를 사용하여 선언되고 다음의 형식을 가집니다:

타입 별칭이 선언된 후 별칭된 이름 (name) 은 프로그램에서 기존 타입 (existing type) 대신에 사용될 수 있습니다. 기존 타입 (existing type) 은 명명된 타입 또는 복합 타입일 수 있습니다. 타입 별칭은 새로운 타입을 생성하지 않습니다; 간단하게 기존 타입을 참조하도록 이름을 허용합니다.

타입 별칭 선언은 기존 제너릭 타입에 이름을 제공하기 위해 제너릭 파라미터를 사용할 수 있습니다. 타입 별칭은 기존 타입의 일부 또는 모든 제너릭 파라미터에 대한 구체적인 타입을 제공할 수 있습니다:

typealias StringDictionary<Value> = Dictionary<String, Value>
// The following dictionaries have the same type.
var dictionary1: StringDictionary<Int> = [:]
var dictionary2: Dictionary<String, Int> = [:]

타입 별칭이 제너릭 파라미터로 선언되면 파라미터의 제약조건은 기존 타입의 제너릭 파라미터의 제약조건과 완벽하게 일치해야 합니다. 예를 들어:

typealias DictionaryOfInts<Key: Hashable> = Dictionary<Key, Int>

타입 별칭과 기존 타입은 서로 바꿔서 사용될 수 있으므로 타입 별칭은 추가로 제너릭 제약조건을 도입할 수 없습니다.

타입 별칭은 선언에서 모든 제너릭 파라미터를 생략하여 기존 타입의 제너릭 파라미터를 전달할 수 있습니다. 예를 들어 여기 선언한 Diccionario 타입 별칭은 Dictionary 와 동일한 제너릭 파라미터와 제약조건을 가집니다.

typealias Diccionario = Dictionary

프로토콜 선언 내에 타입 별칭은 자주 사용되는 타입에 더 짧고 더 편리한 이름으로 제공할 수 있습니다. 예를 들어:

protocol Sequence {
associatedtype Iterator: IteratorProtocol
typealias Element = Iterator.Element
}
func sum<T: Sequence>(_ sequence: T) -> Int where T.Element == Int {
// ...
}

타입 별칭이 없다면 sum 함수는 T.Element 대신에 T.Iterator.Element 로 연관된 타입을 참조해야 합니다.

프로토콜 관련 타입 선언 (Protocol Associated Type Declaration) 을 참고 바랍니다.

GRAMMAR OF A TYPE ALIAS DECLARATION typealias-declaration → attributes opt_{opt} access-level-modifier opt_{opt} typealias typealias-name generic-parameter-clause opt_{opt} typealias-assignment typealias-name → identifier typealias-assignment → = type

함수 선언 (Function Declaration)

함수 선언 (function declaration) 은 프로그램에 함수 또는 메서드를 도입합니다. 클래스, 구조체, 열거형, 또는 프로토콜의 컨텍스트에 선언된 함수는 메서드 (method) 로 참조됩니다. 함수 선언은 func 키워드를 사용하여 선언되고 다음의 형식을 가집니다:

함수가 Void 의 반환 타입을 가지면 반환 타입은 다음과 같이 생략될 수 있습니다:

각 파라미터의 타입은 유추될 수 없으므로 반드시 도입되어야 합니다. 파라미터의 타입 앞에 inout 을 작성하면 파라미터는 함수의 범위 내에서 수정될 수 있습니다. In-out 파라미터는 아래 In-Out 파라미터 (In-Out Parameters) 에서 자세하게 설명되어 있습니다.

구문 (statements) 에 단일 표현식만 포함된 함수 선언은 해당 표현식의 값을 반환하는 것으로 이해됩니다. 이 암시적 반환 구문은 표현식의 타입과 함수의 반환 타입이 Void 가 아니고 어떠한 케이스가 아닌 Never 와 같은 열거형이 아닌 경우에만 간주됩니다.

함수는 함수의 반환 타입으로 튜플 타입을 사용하여 여러값을 반환할 수 있습니다.

함수 정의는 다른 함수 선언 안에 나타날 수 있습니다. 이러한 함수를 중첩 함수 (nested function) 이라 합니다.

중첩 함수는 in-out 파라미터와 같이 절대 이스케이프되지 않는 값을 캡처하거나 비이스케이프 함수 인수로 전달된 경우 비이스케이프 입니다. 그렇지 않으면 중첩 함수는 이스케이프 함수 입니다.

중첩된 함수에 대한 내용은 중첩 함수 (Nested Functions) 를 참고 바랍니다.

파라미터 이름 (Parameter Names)

함수 파라미터는 각 파라미터가 여러 형식 중 하나를 갖는 콤마로 구분된 목록입니다. 함수 호출에서 인수의 순서는 함수의 선언 내에 파라미터의 순서와 일치해야 합니다. 파라미터 목록에서 가장 간단한 형식은 다음과 같습니다:

파라미터는 함수 바디 내에서 사용되는 이름 뿐만 아니라 함수 또는 메서드 호출할 때 사용되는 인수 라벨이 있습니다. 기본적으로 파라미터 이름은 인수 라벨로도 사용됩니다. 예를 들어:

func f(x: Int, y: Int) -> Int { return x + y }
f(x: 1, y: 2) // both x and y are labeled

다음의 형식 중 하나로 인수 라벨의 기본 동작을 재정의 할 수 있습니다:

파라미터 이름 전에 이름은 파라미터 이름과 다를 수 있는 명시적 인수 라벨을 파라미터에 제공합니다. 해당 인수는 함수 또는 메서드 호출에서 주어진 인수 라벨을 사용해야 합니다.

파라미터 이름 전에 언더바 (_) 는 인수 라벨을 숨깁니다. 해당 인수는 함수 또는 메서드 호출에서 라벨이 없어야 합니다.

func repeatGreeting(_ greeting: String, count n: Int) { /* Greet n times */ }
repeatGreeting("Hello, world!", count: 2) // count is labeled, greeting is not

In-Out 파라미터 (In-Out Parameters)

In-out 파라미터는 다음과 같이 전달됩니다:

  1. 함수가 호출될 때 인수의 값은 복사됩니다.

  2. 함수의 바디 내에서 복사본은 수정됩니다.

  3. 함수가 반환될 때 복사본의 값은 기존 인수에 할당됩니다.

이 동작은 copy-in copy-out 또는 call by value 결과 라고 합니다. 예를 들어 계산된 프로퍼티 또는 관찰자가 있는 프로퍼티가 in-out 파라미터로 전달되는 경우 getter 는 함수 호출의 부분으로 호출되고 setter 는 함수 반환의 부분으로 호출됩니다.

최적화로 인수가 메모리의 물리적 주소에 저장된 값인 경우 동일한 메모리 위치가 함수 바디 내부 및 외부에서 사용됩니다. 이런 최적화 동작을 call by reference 라고 합니다; copy-in copy-out 모델의 모든 요구사항을 충족하는 동시에 복사의 오버헤드를 제거합니다. call-by-reference 최적화에 의존하지 않고 copy-in copy-out 에 의해 주어진 모델을 사용하여 작성하면 최적화에 상관없이 올바르게 작동되도록 합니다.

함수 내에서 기존 값이 현재 범위에서 사용가능 하더라도 in-out 인수로 전달된 값은 접근하면 안됩니다. 기존 값에 접근하는 것은 값에 대한 동시 접근이며 Swift 의 메모리 독점 보장을 위반합니다. 같은 이유로 여러개의 in-out 파라미터에 동일한 값을 전달할 수 없습니다.

메모리 안정성과 메모리 독점성에 대한 자세한 내용은 메모리 안정성 (Memory Safety) 을 참고 바랍니다.

in-out 파라미터를 캡처하는 클로저 또는 중첩 함수는 비이스케이프 이어야 합니다. in-out 파라미터를 변경하지 않고 캡처 해야하는 경우 캡처 목록을 사용하여 파라미터를 변경하지 않고 명시적으로 캡처해야 합니다.

func someFunction(a: inout Int) -> () -> Int {
return { [a] in return a + 1 }
}

in-out 파라미터를 캡처하고 변경이 필요한 경우 함수가 반환하기 전에 모든 변경이 완료되었는지 확인하는 멀티 쓰레드 코드와 같이 명시적으로 지역 복사 (local copy) 를 사용합니다.

func multithreadedFunction(queue: DispatchQueue, x: inout Int) {
// Make a local copy and manually copy it back.
var localX = x
defer { x = localX }
// Operate on localX asynchronously, then wait before returning.
queue.async { someMutatingOperation(&localX) }
queue.sync {}
}

in-out 파라미터에 대한 자세한 설명과 예제는 In-Out 파라미터 (In-Out Parameters) 를 참고 바랍니다.

특별한 종류의 파라미터 (Special Kinds of Parameters)

파라미터는 무시될 수 있고 다음의 형식을 사용하여 가변의 값을 가지고 기본값을 제공할 수 있습니다:

언더바 (_) 파라미터는 명시적으로 무시되고 함수의 바디 내에서 접근될 수 없습니다.

기본 타입 이름 바로 뒤에 세 개의 점 (...) 이 오는 파라미터는 가변 파라미터 (variadic parameter) 입니다. 가변 파라미터 뒤에 오는 파라미터는 인수 라벨이 있어야 합니다. 함수는 여러개 가변 파라미터를 가질 수 있습니다. 가변 파라미터는 기본 타입 이름의 요소를 포함한 배열로 처리됩니다. 예를 들어 가변 파라미터 Int...[Int] 로 처리됩니다. 가변 파라미터를 사용하는 예제는 가변 파라미터 (Variadic Parameters) 를 참고 바랍니다.

등호 (=) 가 있는 파라미터와 타입 뒤의 표현식은 주어진 표현식의 기본값을 가지는 것으로 간주됩니다. 함수가 호출될 때 주어진 표현식은 평가됩니다. 함수를 호출할 때 파라미터가 생략되면 기본값이 대신 사용됩니다.

func f(x: Int = 42) -> Int { return x }
f() // Valid, uses default value
f(x: 7) // Valid, uses the value provided
f(7) // Invalid, missing argument label

특별한 종류의 메서드 (Special Kinds of Methods)

self 를 수정하 열거형 또는 구조체에 메서드는 mutating 선언 수식어로 표시되어야 합니다.

상위클래스 메서드를 재정의한 메서드는 override 선언 수식어로 표시되어야 합니다. override 수식어 없이 메서드를 재정의하거나 상위클래스 메서드를 재정의 하지 않는 메서드에 override 수식어를 사용하면 컴파일 에러가 발생합니다.

타입의 인스턴스가 아닌 타입과 관련된 메서드는 열거형과 구조체의 경우 static 선언 수식어나 클래스의 경우 static 또는 class 선언 수식어로 표시되어야 합니다. class 선언 수식어로 표시된 클래스 타입 메서드는 하위클래스 구현에 의해 재정의 될 수 있습니다; class final 또는 static 으로 표시된 클래스 타입 메서드는 재정의 될 수 없습니다.

특별한 이름의 메서드 (Methods with Special Names)

특별한 이름을 가지는 메서드는 함수 호출 구문에 대해 구문 설탕 (syntactic sugar) 을 활성화 합니다. 이러한 메서드 중 하나로 타입을 정의하면 타입의 인스턴스는 함수 호출 구문에서 사용될 수 있습니다. 함수 호출은 해당 인스턴스에서 특별하게 명명된 메서드 중 하나로 호출됩니다.

클래스, 구조체, 또는 열거형 타입은 dynamicCallable 에서 설명 한대로 dynamicallyCall(withArguments:) 메서드 또는 dynamicallyCall(withKeywordArguments:) 메서드를 정의하거나 아래에서 설명 한대로 call-as-function 메서드를 정의하여 함수 호출 구문을 제공할 수 있습니다. 타입이 call-as-function 메서드와 dynamicCallable 속성에 의해 사용되는 메서드 중 하나를 모두 정의하면 컴파일러는 두 메서드를 사용할 수 있는 환경에서 call-as-function 메서드를 더 선호합니다.

call-as-function 메서드의 이름은 callAsFunction() 또는 callAsFunction( 으로 시작하고 라벨이 있거나 없는 인수를 추가하는 이름이 있습니다—예를 들어 callAsFunction(_:_:)callAsFunction(something:) 도 call-as-function 메서드 이름으로 유효합니다.

다음의 함수 호출은 동일합니다:

struct CallableStruct {
var value: Int
func callAsFunction(_ number: Int, scale: Int) {
print(scale * (number + value))
}
}
let callable = CallableStruct(value: 100)
callable(4, scale: 2)
callable.callAsFunction(4, scale: 2)
// Both function calls print 208.

call-as-function 메서드와 dynamicCallable 속성의 메서드는 타입 시스템으로 인코딩하는 정보의 양과 런타임에 가능한 동적 동작의 양 사이에 서로 다른 절충안을 만듭니다. call-as-function 메서드를 선언할 때 인수의 숫자와 각 인수의 타입과 라벨을 지정합니다. dynamicCallable 속성의 메서드는 인수의 배열을 보유하기 위해 사용된 타입만 지정합니다.

call-as-function 메서드 또는 dynamicCallable 속성의 메서드를 정의해도 함수 호출 표현식이 아닌 다른 컨텍스트에서 함수처럼 해당 타입의 인스턴스로 사용할 수 없습니다. 예를 들어:

let someFunction1: (Int, Int) -> Void = callable(_:scale:) // Error
let someFunction2: (Int, Int) -> Void = callable.callAsFunction(_:scale:)

subscript(dynamicMemberLookup:) 서브 스크립트는 dynamicMemberLookup 에서 설명 한대로 멤버 조회를 위한 구문 설탕을 활성화 합니다.

함수와 메서드 던지기 (Throwing Functions and Methods)

에러를 던질 수 있는 함수와 메서드는 throws 키워드로 표시되어야 합니다. 이러한 함수와 메서드는 던지는 함수 (throwing functions)던지는 메서드 (throwing methods) 라고 합니다. 다음의 형식을 가집니다:

던지는 함수 또는 메서드 호출은 try 또는 try! 표현식 (try 또는 try! 연산자의 범위 내) 으로 래핑되어야 합니다.

throws 키워드는 함수의 타입의 일부분이고 던지지 않는 함수는 던지는 함수의 하위 타입입니다. 결과적으로 던지는 함수와 같은 위치에서 던지지 않는 함수를 사용할 수 있습니다.

함수가 에러를 발생할 수 있는지 여부를 기준으로 함수를 오버로드 할 수 없습니다. 이 말은 함수 파라미터 (parameter) 가 에러를 발생할 수 있는 여부에 따라 함수를 오버로드 할 수 있습니다.

던지는 메서드는 던지지 않는 메서드를 재정의할 수 없고 던지는 메서드는 던지지 않는 메서드에 대해 프로토콜 요구사항을 충족할 수 없습니다. 이 말은 던지지 않는 메서드는 던지는 메서드를 재정의할 수 있고 던지지 않는 함수는 던지는 메서드에 대한 프로토콜 요구사항을 충족할 수 있습니다.

다시 던지는 함수와 메서드 (Rethrowing Functions and Methods)

함수 또는 메서드는 함수 파라미터 중 하나만 에러를 발생하는 것을 나타내기 위해 rethrows 키워드로 선언될 수 있습니다. 이러한 함수와 메서드는 다시 던지는 함수 (rethrowing functions)다시 던지는 메서드 (rethrowing methods) 라고 합니다. 다시 던지는 함수와 메서드는 적어도 하나의 던지는 함수 파라미터를 가져야 합니다.

func someFunction(callback: () throws -> Void) rethrows {
try callback()
}

다시 던지는 함수 또는 메서드는 catch 절 안에만 throw 구문을 포함할 수 있습니다. 이렇게 하면 do-catch 구문 내에서 던지는 함수를 호출하고 다른 에러를 발생시켜 catch 절의 에러를 처리할 수 있습니다. 또한 catch 절은 다시 던지는 함수의 던지는 파라미터 중 하나에서 발생한 에러만 처리해야 합니다. 예를 들어 다음은 catch 절이 alwaysThrows() 에서 던져진 에러를 처리하므로 유효하지 않습니다.

func alwaysThrows() throws {
throw SomeError.error
}
func someFunction(callback: () throws -> Void) rethrows {
do {
try callback()
try alwaysThrows() // Invalid, alwaysThrows() isn't a throwing parameter
} catch {
throw AnotherError.error
}
}

던지는 메서드는 다시 던지는 메서드를 재정의할 수 없고 던지는 메서드는 다시 던지는 메서드에 대한 프로토콜 요구사항을 충족할 수 없습니다. 이 말은 다시 던지는 메서드는 던지는 메서드를 재정의할 수 있고 다시 던지는 메서드는 던지는 메서드에 대해 프로토콜 요구사항을 충족할 수 있습니다.

반환되지 않는 함수 (Functions that Never Return)

Swift 는 함수 또는 메서드가 호출자에게 반환하지 않음을 나타내는 Never 타입을 정의합니다. Never 반환 타입이 있는 함수와 메서드는 비반환 (nonreturning) 이라고 합니다. 비반환 함수와 메서드는 복구할 수 없는 에러를 발생하거나 무한으로 계속되는 작업을 시작합니다. 이것은 호출 직후 코드가 실행되지 않음을 뜻합니다. 던지고 다시 던지는 함수는 비반환인 경우에도 적절한 catch 블럭으로 프로그램 제어를 전송할 수 있습니다.

비반환 함수 또는 메서드는 Guard 구문 (Guard Statement) 에서 설명 한대로 guard 구문의 else 절을 끝낼 수 있습니다.

비반환 메서드를 재정의할 수 있지만 새로운 메서드는 반환 타입과 비반환 동작을 유지해야 합니다.

GRAMMAR OF A FUNCTION DECLARATION function-declaration → function-head function-name generic-parameter-clause opt_{opt} function-signaturegeneric-where-clause opt_{opt} function-body opt_{opt} function-head → attributes opt_{opt} declaration-modifiers opt_{opt} func function-name → identifier | operator function-signature → parameter-clause throws opt_{opt} function-result opt_{opt} function-signature → parameter-clause rethrows function-result opt_{opt} function-result → -> attributes opt_{opt} type function-body → code-block parameter-clause → ( ) | ( parameter-list ) parameter-list → parameter | parameter , parameter-list parameter → external-parameter-name opt_{opt} local-parameter-name type-annotation default-argument-clause opt_{opt} parameter → external-parameter-name opt_{opt} local-parameter-name type-annotation parameter → external-parameter-name opt_{opt} local-parameter-name type-annotation ... external-parameter-name → identifier local-parameter-name → identifier default-argument-clause → = expression

열거형 선언 (Enumeration Declaration)

열거형 선언 (enumeration declaration) 은 프로그램에 명명된 열거형 타입을 도입합니다.

열거형 선언은 두 가지 기본 형식을 가지고 있고 enum 키워드를 사용하여 선언됩니다. 두 형식 중 하나를 사용하여 선언된 열거형의 바디는 열거형 케이스 (enumeration cases) 라고 불리는 없거나 더 많은 값과 계산된 프로퍼티, 인스턴스 메서드, 타입 메서드, 초기화 구문, 타입 별칭, 그리고 다른 열거형, 구조체, 그리고 클래스 선언을 포함합니다. 열거형 선언은 초기화 해제 구문 또는 프로토콜 선언을 포함할 수 없습니다.

열거형 타입은 여러 프로토콜을 채택할 수 있지만 클래스, 구조체, 또는 다른 열거형을 상속할 수 없습니다.

클래스와 구조체와 다르게 열거형 타입은 암시적으로 제공된 기본 초기화 구문을 가지지 않습니다; 모든 초기화 구문은 명시적으로 선언되어야 합니다. 초기화 구문은 열거형 내에 다른 초기화 구문으로 위임할 수 있지만 초기화 프로세스는 초기화 구문이 열거형 케이스 중 하나를 self 에 할당 한 후에만 완료됩니다.

구조체와 비슷하지만 클래스와 달리 열거형은 값 타입입니다; 열거형의 인스턴스는 변수 또는 상수에 할당될 때나 함수 호출에 인수로 전달될 때 복사됩니다. 값 타입에 대한 자세한 내용은 구조체와 열거형은 값 타입 (Structures and Enumerations Are Value Types) 를 참고 바랍니다.

확장 선언 (Extension Declaration) 에서 설명 한대로 확장 선언으로 열거형 타입의 동작을 확장할 수 있습니다.

모든 타입의 케이스가 있는 열거형 (Enumerations with Cases of Any Type)

다음의 형식은 모든 타입의 열거형 케이스를 포함하는 열거형 타입을 선언합니다:

이 형식으로 선언된 열거형은 다른 프로그래밍 언어에서 구별된 공용체 (discriminated unions) 라고도 합니다.

이 형식에서 각 케이스 블럭은 case 키워드 다음에 콤마로 구분된 하나 이상의 열거형 케이스로 구성됩니다. 각 케이스의 이름은 고유해야 합니다. 각 케이스는 주어진 타입의 값을 저장하도록 지정할 수도 있습니다. 이러한 타입은 케이스의 이름 바로 다음에 연관된 값 타입 (associated value types) 튜플로 지정됩니다.

연관된 값을 저장하는 열거형 케이스는 지정된 연관된 값을 사용하여 열거형의 인스턴스를 생성하는 함수로 사용될 수 있습니다. 함수와 마찬가지로 코드에 열거형 케이스에 대한 참조를 가져오고 적용할 수 있습니다.

enum Number {
case integer(Int)
case real(Double)
}
let f = Number.integer
// f is a function of type (Int) -> Number
// Apply f to create an array of Number instances with integer values
let evenInts: [Number] = [0, 2, 4, 6].map(f)

연관된 값 타입을 사용하는 케이스의 자세한 내용과 예제는 연관된 값 (Associated Values) 를 참고 바랍니다.

간접 열거형 (Enumerations with Indirection)

열거형은 재귀적 구조체를 가질 수 있습니다. 이 말은 열거형 타입 자체의 인스턴스 인 연관된 값을 사용하는 케이스를 가질 수 있다는 의미입니다. 그러나 열거형 타입의 인스턴스는 메모리에 고정된 레이아웃이 있음을 의미하는 값 의미론을 가지고 있습니다. 재귀를 지원하려면 컴파일러는 간접 레이러를 삽입해야 합니다.

특정 열거형 케이스에 대해 간접을 사용하려면 indirect 선언 수식어로 표시해야 합니다. 간접 케이스는 연관된 값을 가지고 있어야 합니다.

enum Tree<T> {
case empty
indirect case node(value: T, left: Tree, right: Tree)
}

연관된 값을 가진 열거형의 모든 케이스에 대해 간접을 사용하려면 전체 열거형을 indirect 수식어로 표시합니다—이것은 열거형이 케이스 indirect 수식어를 표시해야 될 케이스를 많이 포함하는 경우 편리합니다.

indirect 수식어로 표시된 열거형은 연관된 값을 가진 케이스와 그렇지 않은 케이스를 포함할 수 있습니다. 이 말은 indirect 수식어로 표시된 케이스는 포함할 수 없다는 의미입니다.

원시값 타입의 케이스를 가진 열거형 (Enumerations with Cases of a Raw-Value Type)

다음의 형식은 동일한 기본 타입의 열거형 케이스를 포함하는 열거형 타입을 선언합니다:

이 형식에서 각 케이스 블럭은 case 키워드 다음에 콤마로 구분된 하나 이상의 열거형 케이스로 구성됩니다. 첫번째 형식의 케이스와 달리 각 케이스는 동일한 기본 타입의 원시값 (raw value) 라는 기본값을 가집니다. 이러한 값의 타입은 원시-값 타입 (raw-value type) 으로 지정되고 정수, 부동 소수점 숫자, 문자열 또는 단일 문자로 표현되어야 합니다. 특히 원시-값 타입 (raw-value type)Equatable 프로토콜과 다음 프로토콜 중 하나를 준수해야 합니다: 정수 리터럴에 대한 ExpressibleByIntegerLiteral, 부동 소수점 리터럴에 대한 ExpressibleByFloatLiteral, 문자의 모든 숫자를 포함하는 문자열 리터럴에 대한 ExpressibleByStringLiteral, 그리고 단일 문자만 포함하는 문자열 리터럴에 대한 ExpressibleByUnicodeScalarLiteral 또는 ExpressibleByExtendedGraphemeClusterLiteral. 각 케이스는 고유한 이름을 가지고 고유한 원시값이 할당 되어야 합니다.

원시-값 타입이 Int 로 지정되고 케이스에 명시적으로 값을 할당하지 않으면 암시적으로 값 0, 1, 2, 등으로 할당됩니다. 타입 Int 의 각 할당되지 않은 케이스는 이전 케이스의 원시값에서 자동으로 증가된 원시값으로 할당됩니다.

enum ExampleEnum: Int {
case a, b, c = 5, d
}

위의 예제에서 ExampleEnum.a 의 원시값은 0 이고 ExampleEnum.b 의 값은 1 입니다. 그리고 ExampleEnum.c 의 값이 명시적으로 5 로 설정되어 있으므로 ExampleEnum.d 의 값은 5 에서 자동적으로 증가되어 6 입니다.

원시-값 타입이 String 으로 지정되고 명시적으로 케이스에 값을 할당하지 않으면 각 할당되지 않은 케이스는 암시적으로 케이스의 이름과 동일한 텍스트 문자열로 할당됩니다.

enum GamePlayMode: String {
case cooperative, individual, competitive
}

위의 예제에서 GamePlayMode.cooperative 의 원시값은 "cooperative", GamePlayMode.individual 의 원시값은 "individual", 그리고 GamePlayMode.competitive"competitive" 입니다.

암시적으로 원시-값 타입의 케이스를 가지는 열거형은 Swift 표준 라이브러리에 정의된 RawRepresentable 프로토콜을 준수합니다. 결과적으로 rawValue 프로퍼티를 가지고 있고 init?(rawValue: RawValue) 인 실패 가능한 초기화 구문을 가집니다. ExampleEnum.b.rawValue 처럼 열거형 케이스의 원시값에 접근하기 위해 rawValue 프로퍼티를 사용할 수 있습니다. 옵셔널 케이스를 반환하는 ExampleEnum(rawValue: 5) 와 같이 열거형의 실패 가능한 초기화 구문을 호출하여 해당 케이스를 찾기위해 원시값을 사용할 수도 있습니다. 원시-값 타입이 있는 케이스의 자세한 내용과 예제는 원시값 (Raw Values) 을 참고 바랍니다.

열거형 케이스 접근 (Accessing Enumeration Cases)

열거형 타입의 케이스를 참조하기 위해 EnuerationType.enumerationCase 와 같이 점 (.) 구문을 사용합니다. 열거형 타입이 컨텍스트에서 유추될 수 있으면 열거형 구문 (Enumeration Syntax)암시적 멤버 표현식 (Implicit Member Expression) 에서 설명 한대로 점은 그대로 유지한 채 생략할 수 있습니다.

열거형 케이스의 값을 검사하려면 Switch 구문에서 열거형 값 일치 (Matching Enumeration Values with a Switch Statement) 에서 봤듯이 switch 구문을 사용합니다. 열거형 타입은 열거형 케이스 패턴 (Enumeration Case Pattern) 에서 설명 한대로 switch 구문의 케이스 블럭에서 열거형 케이스 패턴에 대해 일치합니다.

GRAMMAR OF AN ENUMERATION DECLARATION enum-declaration → attributes opt_{opt} access-level-modifier opt_{opt} union-style-enum enum-declaration → attributes opt_{opt} access-level-modifier opt_{opt} raw-value-style-enum union-style-enum → indirect opt_{opt} enum enum-name generic-parameter-clause opt_{opt} type-inheritance-clause opt_{opt} generic-where-clause opt_{opt} { union-style-enum-members opt_{opt} } union-style-enum-members → union-style-enum-member union-style-enum-members opt_{opt} union-style-enum-member → declaration | union-style-enum-case-clause | compiler-control-statement union-style-enum-case-clause → attributes opt_{opt} indirect opt_{opt} case union-style-enum-case-list union-style-enum-case-list → union-style-enum-case | union-style-enum-case , union-style-enum-case-list union-style-enum-case → enum-case-name tuple-type opt_{opt} enum-name → identifier enum-case-name → identifier raw-value-style-enum → enum enum-name generic-parameter-clause opt_{opt} type-inheritance-clausegeneric-where-clause opt_{opt} { raw-value-style-enum-members } raw-value-style-enum-members → raw-value-style-enum-member raw-value-style-enum-members opt_{opt} raw-value-style-enum-member → declaration | raw-value-style-enum-case-clause | compiler-control-statement raw-value-style-enum-case-clause → attributes opt_{opt} case raw-value-style-enum-case-list raw-value-style-enum-case-list → raw-value-style-enum-case | raw-value-style-enum-case , raw-value-style-enum-case-list raw-value-style-enum-case → enum-case-name raw-value-assignment opt_{opt} raw-value-assignment → = raw-value-literal raw-value-literal → numeric-literal | static-string-literal | boolean-literal

구조체 선언 (Structure Declaration)

구조체 선언 (structure declaration) 은 프로그램에 명명된 구조체 타입을 도입합니다. 구조체 선언은 struct 키워드를 사용하여 선언되고 아래의 형식을 가집니다:

구조체의 바디는 선언 (declarations) 이 없거나 많이 포함합니다. 이러한 선언 (declarations) 은 저장된 프로퍼티와 계산된 프로퍼티, 타입 프로퍼티, 인스턴스 메서드, 타입 메서드, 초기화 구문, 서브 스크립트, 타입 별칭, 그리고 다른 구조체, 클래스, 그리고 열거형 선언을 포함할 수 있습니다. 구조체 선언은 초기화 해제 구문 또는 프로토콜 선언을 포함할 수 없습니다. 여러 종류의 선언을 포함한 구조체에 대한 자세한 내용과 예제는 구조체와 클래스 (Structures and Classes) 를 참고 바랍니다.

구조체 타입은 여러 프로토콜을 채택할 수 있지만 클래스, 열거형, 또는 다른 구조체를 상속할 수 없습니다.

이전에 선언된 구조체의 인스턴스를 생성하는 방법은 세가지가 있습니다:

구조체에 선언된 프로퍼티 초기화 프로세스는 초기화 (Initialization) 에 설명되어 있습니다.

구조체 인스턴스의 프로퍼티는 프로퍼티 접근 (Accessing Properties) 에서 설명 한대로 점 (.) 구문을 사용하여 접근할 수 있습니다.

구조체는 값 타입 입니다; 변수나 상수에 할당될 때나 함수 호출에 대해 인수로 전달될 때 구조체의 인스턴스는 복사됩니다. 값 타입에 대한 자세한 내용은 구조체와 열거형은 값 타입 (Structures and Enumerations Are Value Types) 을 참고 바랍니다.

확장 선언 (Extension Declaration) 에서 설명 한대로 확장 선언으로 구조체 타입의 동작을 확장 할 수 있습니다.

GRAMMAR OF A STRUCTURE DECLARATION struct-declaration → attributes opt_{opt} access-level-modifier opt_{opt} struct struct-name generic-parameter-clause opt_{opt} type-inheritance-clause opt_{opt} generic-where-clause opt_{opt} struct-body struct-name → identifier struct-body → { struct-members opt_{opt} } struct-members → struct-member struct-members opt_{opt} struct-member → declaration | compiler-control-statement

클래스 선언 (Class Declaration)

클래스 선언 (class declaration) 은 프로그램에 명명된 클래스 타입을 도입합니다. 클래스 선언은 class 키워드를 사용하여 선언되고 다음의 형식을 가집니다:

클래스의 바디는 선언이 없거나 하나 이상의 선언 (declarations) 을 포함합니다. 이러한 선언 (declarations) 은 저장된 프로퍼티와 계산된 프로퍼티, 인스턴스 메서드, 타입 메서드, 초기화 구문, 하나의 초기화 해제 구문, 서브 스크립트, 타입 별칭, 그리고 다른 클래스, 구조체, 그리고 열거형 선언을 포함할 수 있습니다. 클래스 선언은 프로토콜 선언을 포함할 수 없습니다. 여러종류의 선언을 포함하는 클래스의 자세한 설명과 예제는 구조체와 클래스 (Structures and Classes) 를 참고 바랍니다.

클래스 타입은 상위클래스 (superclass) 로 하나의 부모 클래스만 상속할 수 있지만 프로토콜은 여러개 채택할 수 있습니다. 상위클래스 (superclass)클래스 이름 (class name) 과 콜론 다음에 첫번째로 나타나고 다음으로 채택된 프로토콜 (adopted protocols) 이 나타납니다. 제너릭 클래스 (generic class) 는 다른 제너릭과 제너릭이 아닌 클래스를 상속할 수 있지만 제너릭이 아닌 클래스 (nongeneric class) 는 다른 제너릭이 아닌 클래스만 상속할 수 있습니다. 콜론 뒤에 상위 제너릭 클래스의 이름을 작성할 때 제너릭 파라미터 절을 포함하는 제너릭 클래스의 전체 이름을 포함해야 합니다.

초기화 구문 선언 (Initializer Declaration) 에서 설명 한대로 클래스는 지정된 초기화 구문 (designated initializers) 과 편의 초기화 구문 (convenience initializers) 을 가질 수 있습니다. 클래스의 지정된 초기화 구문은 모든 클래스의 선언된 프로퍼티가 초기화 되어야 하고 상위클래스의 지정된 초기화 구문을 호출하기 전에 수행되어야 합니다.

클래스는 상위클래스의 프로퍼티, 메서드, 서브 스크립트, 그리고 초기화 구문을 재정의 할 수 있습니다. 프로퍼티, 메서드, 서브 스크립트, 그리고 지정된 초기화 구문 재정의는 override 선언 수식어로 표시되어야 합니다.

하위클래스가 상위클래스의 초기화 구문 구현을 요구하려면 상위클래스의 초기화 구문을 required 선언 수식어로 표시해야 합니다. 해당 초기화 구문의 하위클래스의 구현도 required 선언 수식어로 표시되어야 합니다.

상위클래스 (superclass) 에 선언된 프로퍼티와 메서드가 현재 클래스에 의해 상속되더라도 상위클래스 (superclass) 에 선언된 지정된 초기화 구문은 자동 초기화 구문 상속 (Automatic Initializer Inheritance) 에서 설명 한대로 하위클래스가 조건이 충족될 때만 상속됩니다. Swift 클래스는 범용 기본 클래스는 상속하지 않습니다.

이전에 선언된 클래스의 인스턴스를 생성하는 두가지 방법이 있습니다:

프로퍼티 접근 (Accessing Properties) 에 설명 한대로 점 (.) 구문으로 클래스 인스턴스의 프로퍼티에 접근 할 수 있습니다.

클래스는 참조 타입입니다; 클래스의 인스턴스는 변수나 상수에 할당되거나 함수 호출에 인스로 전달될 때 복사가 아닌 참조됩니다. 참조 타입에 대한 자세한 내용은 구조체와 열거형은 값 타입 (Structures and Enumerations Are Value Types) 을 참고 바랍니다.

확장 선언 (Extension Declaration) 에서 설명 한대로 확장 선언으로 클래스 타입의 동작을 확장할 수 있습니다.

GRAMMAR OF A CLASS DECLARATION class-declaration → attributes opt_{opt} access-level-modifier opt_{opt} final opt_{opt} class class-name generic-parameter-clause opt_{opt} type-inheritance-clause opt_{opt} generic-where-clause opt_{opt} class-body class-declaration → attributes opt_{opt} final access-level-modifier opt_{opt} class class-name generic-parameter-clause opt_{opt} type-inheritance-clause opt_{opt} generic-where-clause opt_{opt} class-body class-name → identifier class-body → { class-members opt_{opt} } class-members → class-member class-members opt_{opt} class-member → declaration | compiler-control-statement

프로토콜 선언 (Protocol Declaration)

프로토콜 선언 (protocol declaration) 은 프로그램에 명명된 프로토콜 타입을 도입합니다. 프로토콜 선언은 protocol 키워드를 사용하여 전역에 선언되고 다음의 형식을 가집니다:

프로토콜의 바디는 프로토콜을 채택하는 모든 타입이 충족해야 하는 준수성 요구사항을 설명하는 프로토콜 멤버 선언 (protocol member declarations) 이 없거나 하나 이상의 프로토콜 멤버 선언 (protocol member declarations) 을 포함합니다. 특히 프로토콜은 준수하는 타입이 특정 프로퍼티, 메서드, 초기화 구문, 그리고 서브 스크립트를 구현해야 한다고 선언할 수 있습니다. 프로토콜은 프로토콜의 여러 선언의 관계를 지정할 수 있는 연관된 타입 (associated types) 이라는 특별한 종류의 타입 별칭도 선언할 수 있습니다. 프로토콜 선언은 클래스, 구조체, 열거형, 또는 다른 프로토콜 선언을 포함할 수 없습니다. 프로토콜 멤버 선언 (protocol member declarations) 은 아래 자세하게 설명되어 있습니다.

프로토콜 타입은 다른 프로토콜을 상속할 수 있습니다. 프로토콜 타입이 다른 프로토콜을 상속할 때 다른 프로토콜의 요구사항이 집계되고 현재 프로토콜에서 상속한 모든 타입은 모든 요구사항을 준수해야 합니다. 프로토콜 상속 사용법에 대한 예제는 프로토콜 상속 (Protocol Inheritance) 을 참고 바랍니다.

NOTE 프로토콜 구성 타입 (Protocol Composition Type)프로토콜 구성 (Protocol Composition) 에서 설명 한대로 프로토콜 구성 타입을 사용하여 여러개 프로토콜의 준수성 요구사항을 집계할 수도 있습니다.

해당 타입에 확장 선언에서 프로토콜을 채택하기 위해 이전에 선언된 타입에 프로토콜 준수를 추가할 수 있습니다. 확장에서 채택된 프로토콜의 모든 요구사항을 구현해야 합니다. 타입이 이미 모든 요구사항을 구현한 경우 빈 확장 선언의 바디로 남겨둘 수 있습니다.

기본적으로 프로토콜을 준수하는 타입은 프로토콜에 선언된 모든 프로퍼티, 메서드, 그리고 서브 스크립트를 구현해야 합니다. 이 말은 준수하는 타입에 의한 구현이 옵셔널로 지정하기 위해선 optional 선언 수식어로 프로토콜 멤버 선언을 표시해야 한다는 의미입니다. optional 수식어는 objc 속성으로 표시된 멤버와 objc 속성으로 표시된 프로토콜의 멤버에만 적용될 수 있습니다. 결과적으로 클래스 타입만 옵셔널 멤버 요구사항을 포함한 프로토콜을 채택하고 준수할 수 있습니다. optional 선언 수식어 사용에 대한 자세한 내용과 옵셔널 프로토콜 멤버에 어떻게 접근하는지에 대한 가이드—예를 들어 타입이 이를 구현하는지 확실하지 않은 경우—는 옵셔널 프로토콜 요구사항 (Optional Protocol Requirements) 를 참고 바랍니다.

열거형의 케이스는 타입 멤버에 대해 프로토콜 요구사항을 충족할 수 있습니다. 특히, 연관된 값이 없는 열거형 케이스는 Self 타입의 get-only 타입 변수에 대해 프로토콜 요구사항을 충족하고 연관된 값이 있는 열거형 케이스는 파라미터와 인수 라벨이 케이스의 연관된 값과 일치하게 Self 를 반환하는 함수에 대해 프로토콜 요구사항을 충족합니다. 예를 들어:

protocol SomeProtocol {
static var someValue: Self { get }
static func someFunction(x: Int) -> Self
}
enum MyEnum: SomeProtocol {
case someValue
case someFunction(x: Int)
}

클래스 타입에 대해서만 프로토콜의 채택을 제한하려면 콜론 뒤에 상속된 프로토콜 (inherited protocols) 목록에 AnyObject 프로토콜을 추가해야 합니다. 예를 들어 다음의 프로토콜은 클래스 타입에 의해서만 채택될 수 있습니다:

protocol SomeProtocol: AnyObject {
/* Protocol members go here */
}

AnyObject 요구사항으로 표시된 프로토콜을 상속하는 모든 프로토콜은 마찬가지로 클래스 타입에 의해서만 채택될 수 있습니다.

NOTE objc 속성으로 표시된 프로토콜은 해당 프로토콜에 암시적으로 AnyObject 요구사항이 적용됩니다; 명시적으로 AnyObject 요구사항을 프로토콜에 표시할 필요가 없습니다.

프로토콜은 명명된 타입 이므로 타입으로의 프로토콜 (Protocols as Types) 에서 설명 한대로 다른 명명된 타입으로 코드의 동일한 위치에 나타날 수 있습니다. 그러나 프로토콜은 실제로 지정하는 요구사항에 대해 구현을 제공하지 않으므로 프로토콜의 인스턴스를 구성할 수 없습니다.

위임 (Delegation) 에서 설명 한대로 프로토콜을 사용하여 클래스 또는 구조체의 위임이 구현해야 하는 메서드를 선언할 수 있습니다.

GRAMMAR OF A PROTOCOL DECLARATION protocol-declaration → attributes opt_{opt} access-level-modifier opt_{opt} protocol protocol-name type-inheritance-clause opt_{opt} generic-where-clause opt_{opt} protocol-body protocol-name → identifier protocol-body → { protocol-members opt_{opt} } protocol-members → protocol-member protocol-members opt_{opt} protocol-member → protocol-member-declaration | compiler-control-statement protocol-member-declaration → protocol-property-declaration protocol-member-declaration → protocol-method-declaration protocol-member-declaration → protocol-initializer-declaration protocol-member-declaration → protocol-subscript-declaration protocol-member-declaration → protocol-associated-type-declaration protocol-member-declaration → typealias-declaration

프로토콜 프로퍼티 선언 (Protocol Property Declaration)

프로토콜은 프로토콜 선언 바디에 프로토콜 프로퍼티 선언 (protocol property declaration) 을 포함하여 준수 타입이 프로퍼티를 구현해야 된다고 선언합니다. 프로토콜 프로퍼티 선언은 변수 선언에 특별한 형식을 가집니다:

다른 프로토콜 멤버 선언과 마찬가지로 이러한 프로퍼티 선언은 프로토콜을 준수하는 타입에 대한 getter 와 setter 요구사항만 선언합니다. 결과적으로 선언된 프로토콜 내에 직접적으로 getter 와 setter 를 구현할 수 없습니다.

getter 와 setter 요구사항은 다양한 방법으로 준수하는 타입으로 충족될 수 있습니다. 프로퍼티 선언이 getset 키워드를 모두 포함하면 준수하는 타입은 저장된 변수 프로퍼티나 getter 와 setter 모두 구현하는 속성과 같이 읽고 쓰기가 모두 가능한 계산된 프로퍼티로 구현할 수 있습니다. 그러나 프로퍼티 선언은 상수 프로퍼티나 읽기-전용 계산된 프로퍼티로 구현될 수 없습니다. 프로퍼티 선언이 get 키워드만 포함한다면 모든 종류의 프로퍼티로 구현할 수 있습니다. 프로토콜의 프로퍼티 요구사항을 구현하는 준수하는 타입의 예제는 프로퍼티 요구사항 (Property Requirements) 를 참고 바랍니다.

프로토콜 선언에 타입 프로퍼티 요구사항을 선언하려면 static 키워드로 프로퍼티 선언을 표시합니다. 프로토콜을 준수하는 구조체와 열거형은 static 키워드로 프로퍼티를 선언하고 프로토콜을 준수하는 클래스는 static 또는 class 키워드로 프로퍼티를 선언합니다. 구조체, 열거형, 또는 클래스에 프로토콜 준수를 추가하는 확장은 확장하는 타입과 동일한 키워드를 사용합니다. 타입 프로퍼티 요구사항에 대해 기본 구현을 제공하는 확장은 static 키워드를 사용합니다.

변수 선언 (Variable Declaration) 도 참고 바랍니다.

GRAMMAR OF A PROTOCOL PROPERTY DECLARATION protocol-property-declaration → variable-declaration-head variable-name type-annotation getter-setter-keyword-block

프로토콜 메서드 선언 (Protocol Method Declaration)

프로토콜은 준수하는 타입이 프로토콜 선언 바디에 프로토콜 메서드 선언을 포함하기 위해 메서드를 구현해야 함을 선언합니다. 프로토콜 메서드 선언은 두가지를 제외하고 함수 선언과 동일한 형식을 가집니다: 함수 바디와 함수 선언의 일부로 기본 파라미터 값을 제공할 수 없습니다. 프로토콜의 메서드 요구사항을 구현하는 준수하는 타입에 대한 예제는 메서드 요구사항 (Method Requirements) 를 참고 바랍니다.

프로토콜 선언에 클래스 또는 정적 메서드 요구사항을 선언하려면 static 선언 수식어로 메서드 선언을 표시합니다. 프로토콜을 준수하는 구조체와 열거형은 static 키워드로 메서드를 선언하고 프로토콜을 준수하는 클래스는 static 또는 class 키워드로 메서드를 선언합니다. 구조체, 열거형, 또는 클래스에 프로토콜 준수를 추가하는 확장은 확장하는 타입과 동일한 키워드를 사용합니다. 타입 메서드 요구사항에 대한 기본 구현을 제공하는 확장은 static 키워드를 사용합니다.

함수 선언 (Function Declaration) 도 참고 바랍니다.

GRAMMAR OF A PROTOCOL METHOD DECLARATION protocol-method-declaration → function-head function-name generic-parameter-clause opt_{opt} function-signature generic-where-clause opt_{opt}

프로토콜 초기화 구문 선언 (Protocol Initializer Declaration)

프로토콜은 준수하는 타입이 프로토콜 선언 바디에 프로토콜 초기화 구문 선언을 포함하기 위해 초기화 구문 구현을 선언합니다. 프로토콜 초기화 구문 선언은 초기화 구문의 바디를 포함하지 않는다는 점을 제외하고 초기화 구문 선언과 동일한 형식을 가집니다.

준수하는 타입은 실패할 수 없는 초기화 구문 (nonfailable initializer) 또는 init! 실패 가능한 초기화 구문 (failable initializer) 를 구현하여 실패할 수 없는 프로토콜 초기화 구문 요구사항을 충족할 수 있습니다. 준수하는 타입은 모든 종류의 초기화 구문 구현으로 실패 가능한 프로토콜 초기화 구문 요구항을 충족할 수 있습니다.

클래스는 프로토콜의 초기화 구문 요구사항을 충족하기 위해 초기화 구문을 구현할 때 초기화 구문이 final 선언 수식어로 표시되어 있지 않다면 required 선언 수식어로 표시되어야 합니다.

초기화 구문 선언 (Initializer Declaration) 도 참고 바랍니다.

GRAMMAR OF A PROTOCOL INITIALIZER DECLARATION protocol-initializer-declaration → initializer-head generic-parameter-clause opt_{opt} parameter-clausethrowsopt_{opt} generic-where-clause opt_{opt} protocol-initializer-declaration → initializer-head generic-parameter-clause opt_{opt} parameter-clauserethrows generic-where-clause opt_{opt}

프로토콜 서브 스크립트 선언 (Protocol Subscript Declaration)

프롵콜은 준수하는 타입이 프로토콜 선언 바디에 프로토콜 서브 스크립트 선언을 포함하기 위해 서브 스크립트 구현을 선언합니다. 프로토콜 서브 스크립트 선언은 서브 스크립트 선언의 특별한 형식을 가집니다:

서브 스크립트 선언은 프로토콜을 준수하는 타입에 대해 최소한의 getter 와 setter 구현 요구사항만 선언합니다. 서브 스크립트 선언이 getset 키워드 모두 포함한다면 준수하는 타입은 getter 와 setter 절 모두 구현해야 합니다. 서브 스크립트 선언이 get 키워드만 포함한다면 준수하는 타입은 적어도 getter 절은 구현해야 하고 선택적으로 setter 절을 구현할 수 있습니다.

프로토콜 선언에서 정적 서브 스크립트 요구사항을 선언하려면 static 선언 수식어로 서브 스크립트 선언을 표시합니다. 프로토콜은 준수하는 구조체와 열거형은 static 키워드로 서브 스크립트를 선언하고 프로토콜을 준수하는 클래스는 static 또는 class 키워드로 서브 스크립트를 선언합니다. 구조체, 열거형, 또는 클래스에 프로토콜 준수를 추가하는 확장은 확장하는 타입과 동일한 키워드를 사용합니다. 정적 서브 스크립트 요구사항에 대한 기본 구현을 제공하는 확장은 static 키워드를 사용합니다.

서브 스크립트 선언 (Subscript Declaration) 도 참고 바랍니다.

GRAMMAR OF A PROTOCOL SUBSCRIPT DECLARATION protocol-subscript-declaration → subscript-head subscript-result generic-where-clause opt_{opt} getter-setter-keyword-block

프로토콜 연관된 타입 선언 (Protocol Associated Type Declaration)

프로토콜은 associatedtype 키워드를 사용하여 연관된 타입을 선언합니다. 연관된 타입은 프로토콜의 선언의 일부분으로 사용되는 타입에 대한 별칭을 제공합니다. 연관된 타입은 제너릭 파라미터 절에서 타입 파라미터와 유사하지만 선언된 프로토콜에서 Self 와 연관됩니다. 해당 컨텍스트에서 Self 는 프로토콜을 준수하는 최종 타입을 참조합니다. 더 자세한 내용과 예제는 연관된 타입 (Associated Types) 을 참고 바랍니다.

연관된 타입을 재선언 하지 않고 다른 프로토콜에서 상속된 연관된 타입에 제약사항을 추가하려면 프로토콜 선언에 제너릭 where 절을 사용합니다. 예를 들어 아래 SubProtocol 의 선언은 동일합니다:

protocol SomeProtocol {
associatedtype SomeType
}
protocol SubProtocolA: SomeProtocol {
// This syntax produces a warning.
associatedtype SomeType: Equatable
}
// This syntax is preferred.
protocol SubProtocolB: SomeProtocol where SomeType: Equatable { }

타입 별칭 선언 (Type Alias Declaration) 도 참고 바랍니다.

GRAMMAR OF A PROTOCOL ASSOCIATED TYPE DECLARATION protocol-associated-type-declaration → attributes opt_{opt} access-level-modifier opt_{opt} associatedtypetypealias-name type-inheritance-clause opt_{opt} typealias-assignment opt_{opt} generic-where-clause opt_{opt}

초기화 구문 선언 (Initializer Declaration)

초기화 구문 선언 (Initializer declaration) 은 프로그램에 클래스, 구조체, 또는 열거형에 대한 초기화 구문을 도입합니다. 초기화 구문 선언은 init 키워드를 사용하여 선언되고 두 개의 기본 형식을 가지고 있습니다.

구조체, 열거형, 그리고 클래스 타입은 여러개의 초기화 구문을 가질 수 있지만 클래스 초기화 구문에 대한 규칙과 관련 동작은 다릅니다. 구조체와 열거형과 다르게 클래스는 두 종류의 초기화 구문을 가집니다: 초기화 (Initialization) 에서 설명 한대로 지정된 초기화 구문 (designated initializers) 과 편의 초기화 구문 (convenience initializers) 이 있습니다.

다음 형식은 구조체, 열거형, 그리고 클래스의 지정된 초기화 구문에 대한 초기화 구문을 선언합니다:

클래스의 지정된 초기화 구문은 모든 클래스의 프로퍼티를 직접적으로 초기화 합니다. 같은 클래스의 다른 초기화 구문에서 호출할 수 없고 상위클래스를 가지고 있다면 상위클래스의 지정된 초기화 구문 중 하나를 호출해야 합니다. 클래스가 상위클래스에서 프로퍼티를 상속하면 상위클래스의 지정된 초기화 구문 중 하나는 현재 클래스에서 해당 프로퍼티에 설정되거나 수정되기 전에 호출되어야 합니다.

지정된 초기화 구문은 클래스 선언의 컨텍스트에서만 선언될 수 있으므로 확장 선언을 사용하는 클래스에 추가될 수 없습니다.

구조체와 열거형에 초기화 구문은 선언된 다른 초기화 구문을 호출하여 초기화 프로세스의 일부 또는 전체를 위임할 수 있습니다.

클래스에 편의 초기화 구문을 선언하려면 convenience 선언 수식어로 초기화 구문 선언을 표시합니다.

편의 초기화 구문은 초기화 프로세스를 다른 편의 초기화 구문 또는 클래스의 지정된 초기화 구문 중 하나로 위임할 수 있습니다. 이 말은 초기화 프로세스는 궁극적으로 클래스의 프로퍼티를 초기화 하는 지정된 초기화 구문을 호출로 끝나야 한다는 의미입니다. 편의 초기화 구문은 상위클래스의 초기화 구문을 호출할 수 없습니다.

모든 하위클래스에 초기화 구문 구현을 요청하기 위해 required 선언 수식어로 지정된 초기화 구문과 편의 초기화 구문을 표시할 수 있습니다. 해당 초기화 구문의 하위클래스의 구현도 required 선언 수식어로 표시되어야 합니다.

기본적으로 상위클래스에 선언된 초기화 구문은 하위클래스에 의해 상속되지 않습니다. 이 말은 하위클래스는 기본값으로 모든 저장된 프로퍼티를 초기화 하고 자체 초기화 구문을 정의하지 않으면 모든 상위클래스의 초기화 구문을 상속합니다. 하위클래스가 모든 상위클래스의 지정된 초기화 구문을 재정의하면 상위클래스의 편의 초기화 구문을 상속합니다.

메서드, 프로퍼티, 그리고 서브 스크립트와 마찬가지로 override 선언 수식어로 재정의된 지정된 초기화 구문을 표시해야 합니다.

NOTE required 선언 수식어로 초기화 구문을 표시하면 하위클래스에서 필수 초기화 구문을 재정의 할 때 override 수식어로 초기화 구문을 표시하지 않아도 됩니다.

함수와 메서드처럼 초기화 구문은 에러를 던지거나 다시 던질 수 있습니다. 그리고 함수와 메서드처럼 적절한 동작을 나타내기 위해 초기화 구문의 파라미터 뒤에 throws 또는 rethrows 키워드를 사용합니다.

여러 타입 선언에서 초기화 구문의 예제는 초기화 (Initialization) 을 참고 바랍니다.

실패 가능한 초기화 구문 (Failable Initializers)

실패 가능한 초기화 구문 (failable initializer) 은 초기화 구문이 선언된 타입의 옵셔널 인스턴스 또는 암시적 언래핑된 옵셔널 인스턴스를 생성하는 초기화 구문의 타입입니다. 결과적으로 실패 가능한 초기화 구문은 초기화 실패를 나타내는 nil 을 반환할 수 있습니다.

옵셔널 인스턴스를 생성하는 실패 가능한 초기화 구문을 선언하려면 초기화 구문 선언에서 init 키워드 뒤에 물음표를 붙입니다 (init?). 암시적으로 언래핑된 옵셔널 인스턴스를 생성하는 실패 가능한 초기화 구문을 선언하려면 뒤에 느낌표를 붙입니다 (init!). 아래의 예제는 구조체의 옵셔널 인스턴스를 생성하는 실패 가능한 초기화 구문 init? 을 보여줍니다.

struct SomeStruct {
let property: String
// produces an optional instance of 'SomeStruct'
init?(input: String) {
if input.isEmpty {
// discard 'self' and return 'nil'
return nil
}
property = input
}
}

결과의 선택성을 다루는 것을 제외하고 실패없는 초기화 구문 호출과 동일하게 실패 가능한 초기화 구문 init? 을 호출합니다.

if let actualInstance = SomeStruct(input: "Hello") {
// do something with the instance of 'SomeStruct'
} else {
// initialization of 'SomeStruct' failed and the initializer returned 'nil'
}

실패 가능한 초기화 구문은 초기화 구문의 바디의 구현에 어느 위치에서든 nil 을 반환할 수 있습니다.

실패 가능한 초기화 구문은 여러 종류의 초기화 구문에 위임할 수 있습니다. 실패없는 초기화 구문은 다른 실패없는 초기화 구문이나 init! 실패 가능한 초기화 구문으로 위임할 수 있습니다. 실패없는 초기화 구문은 예를 들어 super.init()! 처럼 상위클래스의 초기화 구문에 강제 언래핑한 결과로 init? 실패 가능한 초기화 구문으로 위임할 수 있습니다.

초기화 실패는 초기화 구문 위임으로 전파됩니다. 특히, 실패 가능한 초기화 구문이 실패하고 nil 을 반환하는 초기화 구문에 위임하면 위임된 초기화 구문도 실패하고 암시적으로 nil 을 반환합니다. 실패없는 초기화 구문이 실패하고 nil 을 반환하는 init! 실패 가능한 초기화 구문에 위임하면 nil 값을 가지는 옵셔널을 언래핑 하기위해 ! 연산자를 사용하는 것과 같이 런타임 에러가 발생합니다.

실패 가능한 지정된 초기화 구문은 모든 종류의 지정된 초기화 구문에 의해 하위클래스에서 재정의 될 수 있습니다. 실패없는 지정된 초기화 구문은 실패없는 지정된 초기화 구문에 의해서만 하위클래스에서 재정의 될 수 있습니다.

실패 가능한 초기화 구문에 대한 자세한 내용과 예제는 실패 가능한 초기화 구문 (Failable Initializers) 을 참고 바랍니다.

GRAMMAR OF AN INITIALIZER DECLARATION initializer-declaration → initializer-head generic-parameter-clause opt_{opt} parameter-clause throwsoptgeneric-where-clause opt_{opt} initializer-body initializer-declaration → initializer-head generic-parameter-clause opt_{opt} parameter-clause rethrowsgeneric-where-clause opt_{opt} initializer-body initializer-head → attributes opt_{opt} declaration-modifiers opt_{opt} init initializer-head → attributes opt_{opt} declaration-modifiers opt_{opt} init ? initializer-head → attributes opt_{opt} declaration-modifiers opt_{opt} init ! initializer-body → code-block

초기화 해제 구문 선언 (Deinitializer Declaration)

초기화 해제 구문 선언 (deinitializer declaration) 은 클래스 타입에 대한 초기화 해제 구문을 선언합니다. 초기화 해제 구문은 파라미터를 가지지 않고 다음의 형식을 가집니다:

초기화 해제 구문은 클래스 객체에 어떠한 참조도 없으면 클래스 객체가 할당 해제되기 직전에 자동으로 호출됩니다. 초기화 해제 구문은 클래스 선언의 바디내에만 선언될 수 있지만 클래스의 확장에는 선언될 수 없고 각 클래스는 하나만 가질 수 있습니다.

하위클래스는 하위클래스 객체가 할당 해제되기 직전에 암시적으로 호출되는 상위클래스의 초기화 해제 구문을 상속합니다. 하위클래스 객체는 상속 체인의 모든 초기화 해제 구문이 실행을 완료할 때까지 할당 해제되지 않습니다.

초기화 해제 구문은 직접적으로 호출되지 않습니다.

클래스 선언에서 초기화 해제 구문이 어떻게 사용되는지에 대한 예제는 초기화 해제 (Deinitialization) 를 참고 바랍니다.

GRAMMAR OF A DEINITIALIZER DECLARATION deinitializer-declaration → attributes opt_{opt} deinit code-block

확장 선언 (Extension Declaration)

확장 선언 (extension declaration) 은 기존 타입의 동작을 확장할 수 있습니다. 확장 선언은 extension 키워드를 사용하여 선언되고 다음의 형식을 가집니다:

확장 선언의 바디는 선언 (declarations) 이 하나도 없거나 하나 이상의 선언 (declarations) 을 포함합니다. 이러한 선언 (declaration) 스캔은 계산된 프로퍼티, 계산된 타입 프로퍼티, 인스턴스 메서드, 타입 메서드, 초기화 구문, 서브 스크립트 선언, 그리고 클래스, 구조체, 그리고 열거형 선언도 포함합니다. 확장 선언은 초기화 해제 구문 또는 프로토콜 선언, 저장된 프로퍼티, 프로퍼티 관찰자, 또는 다른 확장 선언은 포함할 수 없습니다. 프로토콜 확장에서 선언은 final 로 표시될 수 없습니다. 여러 종류의 선언을 포함한 확장에 대한 내용과 예제는 확장 (Extensions) 을 참고 바랍니다.

타입 이름 (type name) 이 클래스, 구조체, 또는 열거형 타입 이면 확장은 해당 타입을 확장합니다. 타입 이름름 (type name) 이 프로토콜 타입 이면 확장은 해당 프로토콜을 준수하는 모든 타입을 확장합니다.

제너릭 타입 또는 연관된 타입이 있는 프로토콜을 확장하는 확장 선언은 요구사항 (requirements) 을 포함할 수 있습니다. 확장된 타입 또는 확장된 프로토콜을 준수하는 타입의 인스턴스가 요구사항 (requirements) 을 충족하면 인스턴스는 선언에 지정된 동작을 얻습니다.

확장 선언은 초기화 구문 선언을 포함할 수 있습니다. 이 말은 확장하려는 타입이 다른 모듈에 정의되면 초기화 구문 선언은 해당 타입의 멤버가 올바르게 초기화 되도록 해당 모듈에 이미 정의된 초기화 구문으로 위임해야 합니다.

기존 타입의 프로퍼티, 메서드, 그리고 초기화 구문은 해당 타입의 확장에서 재정의 될 수 없습니다.

확장 선언은 채택된 프로토콜 (adopted protocols) 을 지정하여 기존 클래스, 구조체, 또는 열거형 타입에 프로토콜 준수를 추가할 수 있습니다:

확장 선언은 기존 클래스에 클래스 상속을 추가할 수 없으므로 타입 이름 (type name) 과 콜론 뒤에 프로토콜의 목록만 지정할 수 있습니다.

조건부 준수성 (Conditional Conformance)

조건부로 프로토콜을 준수하기 위해 제너릭 타입을 확장할 수 있기 때문에 타입의 인스턴스가 특정 요구사항만 충족되면 프로토콜을 준수하도록 할 수 있습니다. 확장 선언에 요구사항 (requirements) 를 추가하여 존건부 프로토콜 준수성을 추가합니다.

재정의한 요구사항은 일부 제너릭 컨텍스트에서 사용되지 않음 (Overridden Requirements Aren’t Used in Some Generic Contexts)

일부 제너릭 컨텍스트에서 조건부 프로토콜 준수성에서 동작을 가져오는 타입은 해당 프로토콜의 요구사항의 특별한 구현을 항상 사용하지 않습니다. 이 동작을 설명하기 위해 다음 예제는 두 프로토콜 모두 조건부로 준수하는 두 프로토콜과 제너릭 타입을 정의합니다.

protocol Loggable {
func log()
}
extension Loggable {
func log() {
print(self)
}
}
protocol TitledLoggable: Loggable {
static var logTitle: String { get }
}
extension TitledLoggable {
func log() {
print("\(Self.logTitle): \(self)")
}
}
struct Pair<T>: CustomStringConvertible {
let first: T
let second: T
var description: String {
return "(\(first), \(second))"
}
}
extension Pair: Loggable where T: Loggable { }
extension Pair: TitledLoggable where T: TitledLoggable {
static var logTitle: String {
return "Pair of '\(T.logTitle)'"
}
}
extension String: TitledLoggable {
static var logTitle: String {
return "String"
}
}

Pair 구조체는 제너릭 타입이 Loggable 또는 TitledLoggable 을 각각 준수할 때마다 LoggableTitledLoggable 을 준수합니다. 아래 예제에서 oneAndTwoStringTitledLoggable 을 준수하기 때문에 TitledLoggable 을 준수하는 Pair<String> 의 인스턴스 입니다. oneAndTwo 에서 log() 메서드를 직접 호출하면 타이틀 문자열을 포함하는 특별한 버전이 사용됩니다.

let oneAndTwo = Pair(first: "one", second: "two")
oneAndTwo.log()
// Prints "Pair of 'String': (one, two)"

그러나 oneAndTwo 제너릭 컨텍스트에서 사용되거나 Loggable 프로토콜의 인스턴스로 사용되면 특별한 버전이 사용되지 않습니다. Swift 는 PairLoggable 준수하는데 필요한 최소 요구사항만 참조하여 호출하기 위한 log() 의 구현을 선택합니다. 이러한 이유로 Loggable 프로토콜에 의해 제공된 기본 구현이 대신 사용됩니다.

func doSomething<T: Loggable>(with x: T) {
x.log()
}
doSomething(with: oneAndTwo)
// Prints "(one, two)"

log()doSomething(_:) 에 전달된 인스턴스에서 호출되면 사용자 정의 된 타이틀은 로깅 된 문자열에서 생략됩니다.

프로토콜 준수성은 중복되지 않아야 함 (Protocol Conformance Must Not Be Redundant)

구체적인 타입은 특정 프로토콜을 한 번만 준수 할 수 있습니다. Swift 는 중복 프로토콜 준수를 에러로 표시합니다. 두 가지 상황에서 이러한 에러가 발생할 수 있습니다. 첫번째 상황은 동일한 프로토콜을 여러번 명시적으로 준수하지만 요구사항이 다른 경우입니다. 두번째 상황은 암시적으로 동일한 프로토콜에서 여러번 상속할 때 입니다. 이러한 상황은 아래 섹션에 설명되어 있습니다.

명시적 중복 해결 (Resolving Explicit Redundancy)

구체적인 타입에 여러 확장은 확장의 요구사항이 독립적이더라도 동일한 프로토콜에 대한 준수성을 추가할 수 없습니다. 이 제한은 아래 예제에서 설명됩니다. 두 확장 선언은 Int 요소가 있는 배열에 대해서와 String 요소가 있는 배열에 대해 Serializable 프로토콜의 조건부 준수성을 추가를 시도합니다.

protocol Serializable {
func serialize() -> Any
}
extension Array: Serializable where Element == Int {
func serialize() -> Any {
// implementation
}
}
extension Array: Serializable where Element == String {
func serialize() -> Any {
// implementation
}
}
// Error: redundant conformance of 'Array<Element>' to protocol 'Serializable'

여러 구체적인 타입을 기반으로 조건부 준수성을 추가하려면 각 타입이 준수하는 새로운 프로토콜을 생성하고 조건부 준수를 선언할 때 요구사항으로 해당 프로토콜을 사용합니다.

protocol SerializableInArray { }
extension Int: SerializableInArray { }
extension String: SerializableInArray { }
extension Array: Serializable where Element: SerializableInArray {
func serialize() -> Any {
// implementation
}
}

암시적 중복 해결 (Resolving Implicit Redundancy)

구체적인 타입이 프로토콜을 조건적으로 준수하면 해당 타입은 동일한 요구사항을 가진 모든 부모 프로토콜을 암시적으로 준수합니다.

단일 부모에서 상속하는 두 프로토콜을 조건부로 준수하는 타입이 필요한 경우 명시적으로 부모 프로토콜에 준수성을 선언합니다. 이것은 요구사항이 다른 부모 프로토콜을 암시적으로 두번 준수하는 것을 방지합니다.

다음 예제는 TitledLoggable 과 새로운 MarkedLoggable 프로토콜을 조건부 준수를 선언할 때 충돌을 막기 위해 Array 의 조건부 준수를 Loggable 로 명시적으로 선언합니다.

protocol MarkedLoggable: Loggable {
func markAndLog()
}
extension MarkedLoggable {
func markAndLog() {
print("----------")
log()
}
}
extension Array: Loggable where Element: Loggable { }
extension Array: TitledLoggable where Element: TitledLoggable {
static var logTitle: String {
return "Array of '\(Element.logTitle)'"
}
}
extension Array: MarkedLoggable where Element: MarkedLoggable { }

Loggable 에 대한 조건부 준수성을 명시적으로 선언하는 확장이 없으면 다른 Array 확장이 암시적으로 이러한 선언을 생성하여 에러가 발생합니다:

extension Array: Loggable where Element: TitledLoggable { }
extension Array: Loggable where Element: MarkedLoggable { }
// Error: redundant conformance of 'Array<Element>' to protocol 'Loggable'

GRAMMAR OF AN EXTENSION DECLARATION extension-declaration → attributes opt_{opt} access-level-modifier opt_{opt} extension type-identifier type-inheritance-clause opt_{opt} generic-where-clause opt_{opt} extension-body extension-body → { extension-members opt_{opt} } extension-members → extension-member extension-members opt_{opt} extension-member → declaration | compiler-control-statement

서브 스크립트 선언 (Subscript Declaration)

서브 스크립트 (subscript) 선언은 특정 타입에 대한 객체에 대한 서브 스크립트를 지원을 추가할 수 있고 일반적으로 콜렉션, 리스트, 또는 시퀀스에서 요소의 접근에 대한 편리한 구문을 제공하기 위해 사용됩니다. 서브 스크립트 선언은 subscript 키워드를 사용하여 선언되고 다음의 형식을 가집니다:

서브 스크립트 선언은 클래스, 구조체, 열거형, 확장, 또는 프로토콜 선언의 컨텍스트에서만 나타날 수 있습니다.

파라미터 (parameters) 는 서브 스크립트 표현식에서 해당 타입의 요소에 접근하기 위해 사용되는 하나 이상의 인덱스를 지정합니다 (예를 들어 표현식 object[i] 에서 i). 요소에 접근하기 위해 사용되는 인덱스는 모든 타입이 될 수 있지만 각 파라미터는 각 인덱스의 타입을 지정하기 위해 타입 주석을 포함해야 합니다. 반환 타입 (return type) 은 접근하는 요소의 타입을 지정합니다.

계산된 프로퍼티와 마찬가지로 서브 스크립트 선언은 접근한 요소의 값을 읽고 쓰기를 제공합니다. getter 는 값을 읽기 위해 사용되고 setter 는 값을 쓰기 위해 사용됩니다. setter 절은 선택사항이며 getter 만 필요하다면 모든 절을 생략할 수 있고 직접적으로 요청된 값을 간단하게 반환합니다. 이 말은 setter 절을 제공하면 getter 절을 제공해야만 한다는 의미입니다.

setter 이름 (setter name) 과 둘러싸인 소괄호는 선택사항입니다. setter 이름을 제공하면 setter 의 파라미터의 이름으로 사용됩니다. setter 이름을 제공하지 않으면 setter 의 기본 파라미터 이름은 value 입니다. setter 의 파라미터의 타입은 반환 타입 (return type) 과 동일합니다.

파라미터 (parameters) 또는 반환 타입 (return type) 이 오버로딩 중인 것과 다르면 선언된 타입의 서브 스크립트 선언을 오버로드 할 수 있습니다. 상위클래스에서 상속된 서브 스크립트 선언을 재정의 할 수도 있습니다. 재정의 하면 override 선언 수식어로 재정의된 서브 스크립트 선언을 표시해야 합니다.

서브 스크립트 파라미터는 두가지를 제외하고 함수 파라미터와 동일한 규칙을 따릅니다. 기본 적으로 서브 스크립트에서 사용하는 파라미터는 함수, 메서드, 그리고 초기화 구문과 다르게 인수 라벨을 가지지 않습니다. 그러나 함수, 메서드, 그리고 초기화 구문에서 사용하는 동일한 구문을 사용하여 명시적으로 인수 라벨을 제공할 수 있습니다. 또한 서브 스크립트는 in-out 파라미터를 가질 수 없습니다. 서브 스크립트 파라미터는 특별한 종류의 파라미터 (Special Kinds of Parameters) 에서 설명 한 구문을 사용하여 기본값을 가질 수 있습니다.

프로토콜 서브 스크립트 선언 (Protocol Subscript Declaration) 에서 설명 한대로 프로토콜 선언의 컨텍스트에서 서브 스크립트를 선언할 수도 있습니다.

서브 스크립트에 대한 자세한 내용과 서브 스크립트 선언의 예제는 서브 스크립트 (Subscripts) 를 참고 바랍니다.

타입 스크립트 선언 (Type Subscript Declarations)

타입의 인스턴스가 아닌 타입에 의해 노출되는 서브 스크립트를 선언하려면 static 선언 수식어로 서브 스크립트 선언을 표시합니다. 클래스는 대신 class 선언 수식어로 타입 계산된 프로퍼티를 표시하여 하위클래스가 상위클래스의 구현을 재정의 할 수 있습니다. 클래스 선언에서 static 키워드는 classfinal 선언 수식어 모두 선언에 표시하는 것과 동일한 효과를 가집니다.

GRAMMAR OF A SUBSCRIPT DECLARATION subscript-declaration → subscript-head subscript-result generic-where-clause opt_{opt} code-block subscript-declaration → subscript-head subscript-result generic-where-clause opt_{opt} getter-setter-block subscript-declaration → subscript-head subscript-result generic-where-clause opt_{opt} getter-setter-keyword-block subscript-head → attributes opt_{opt} declaration-modifiers opt_{opt} subscript generic-parameter-clause opt_{opt}