반응형

Swift.org 사이트의 강좌 번역본입니다.

원문 : 애플 개발 문서 Swift 5.7 Language Guide - The Basics

기초(The Basics)

Swift는 iOS, macOS, watchOS, 그리고 tvOS 앱을 개발하기 위한 새로운 프로그래밍 언어입니다. 그럼에도 불구하고(nonetheless), Swift의 많은 부분이 C와 Objective-C에서 개발하던 경험과 익숙할 것입니다.

Swift는 정수형에 대해 Int, 부동 소수점(floating-point) 값에 대해 Double과 Float, 불린 값에 대해 Bool, 텍스트 데이터에 대해 String을 포함하는 모든 기본적인(fundamental) C와 Objective-C 타입의 자체 버젼을 제공합니다. Swift는 또한 Collection Type에서 설명했던, 3개의 기본(primary) 컬렉션 타입 Array, Set, Dictionary의 강력한 버젼을 제공합니다.

C 언어 처럼, Swift는 고유한 이름으로 값을 저장하고 참조하기 위해서 변수를 사용합니다. Swift는 또한 값을 변경할 수 없는 변수를 광범위하게 사용합니다. 이를 상수(constants)라고 하고, C 언어에서 상수보다 훨씬 더 강력합니다. 상수는 값을 변경할 필요가 없는 작업할때, Swift 코드를 안정적으로 만들고 의도를 명확히 하기 위해 전역적으로 사용됩니다.

익숙한 타입들 외에도, Swift는 튜플(tuples)과 같은, Objective-C에서는 찾을수 없는 고급 타입을 도입했습니다. 튜플(Tuples)는 값을 그룹화해서 만들고 전달하는것이 가능합니다. 하나의 함수에서 여러개의 값을 하나로 구성된 값처럼 반환하기 위해 튜플을 사용할 수 있습니다.

Swift는 또한 값이 없는 것을 처리하는, 옵셔널(optional) 타입을 도입했습니다. 옵셔널(Optional)은 그곳에 값이 있고, x와 같습니다 또는 그곳에는 아무런 값이 없습니다 둘 중 하나를 말합니다. 옵셔널은 Objective-C 에서 nil 포인터와 비슷하게 사용하지만, 클래스 뿐만 아니라, 모든 타입에서 동작합니다. Objective-C 에서의 nil 포인터보다 더 안전하고 표현하기 더 좋을 뿐 아니라, Swift의 가장 강력한 많은 기능들의 핵심입니다.

Swift는 타입에 안전한(type-safe) 언어이며, 이는 코드로 작업할 수 있는 값의 타입에 대해 명확하는데 도와주는 언어라는 것을 의미합니다. 코드의 일부가 String을 요구하는 경우, 타입 안전성(type-safety)은 실수로 Int를 전달하는 것을 막아줍니다. 마찬가지로(likewise), 타입 안전성은 옵셔널이 아닌 String을 요구하는 코드에서 옵셔널 String을 실수로 전달하는것을 막아줍니다. 타입 안전성은 개발 과정에서 가능한한 빨리 오류를 찾고 고칠수 있도록 도와줍니다.

상수와 변수(Constants and Variables)

상수와 변수는 이름(maximumNumberOfLoginAttemptes 또는 welcomeMessage)을 특정 타입(숫자 10 또는 문자열 "Hello")의 값과 연관짓습니다(associte). 상수(constant)의 값은 한번 설정되면 변경할 수 없는 반면에(whereas), 변수(variable)은 나중에 다른 값으로 설정할 수 있습니다.

상수와 변수 선언하기(Declaring Constants and Variables)

상수와 변수는 반드시 사용하기 전에 선언되야 합니다. let 키워드로 상수를 선언하고 var 키워드로 변수를 선언합니다. 다음은 사용자가 로그인 시도한 횟수를 추적하기 위해 상수와 변수를 어떻게 사용할수 있는지에 대한 예제 입니다.

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0

이 코드는 다음과 같이 읽을 수 있습니다.

maximumNumberOfLoginAttempts라는 새로운 상수를 선언하고, 값을 10을 줍니다. 그리고나서 currentLoginAttempt라는 새로운 변수를 선언하고, 초기 값으로 0을 줍니다.

이 예제에서, 최대 값은 결코 변하지 않기 때문에, 로그인 시도를 허용하는 최대 횟수는 상수로 선언했습니다. 현재 로그인 시도 횟수는 로그인 시도가 실패한 후에 값이 증가되야 하기 때문에 변수로 선언했습니다.

한줄에 여러개의 상수나 여러개의 변수를 콤마(,)로 구분해서 선언할 수 있습니다.

var x = 0.0, y = 0.0, z = 0.0

주의 코드에서 저장된 값을 변경하지 않는 경우, 항상 let 키워드를 사용해서 상수로 선언하세요. 저장하는 값이 변경할 필요가 있을때에만 변수를 사용하세요.

타입 명시하기(Type Annotations)

상수나 변수가 저장할 수 있는 값의 종류를 명확하게 하기 위해서, 상수나 변수를 선언할때 타입 명시(type annotation)를 제공할 수 있습니다. 상수나 변수 이름 뒤에 콜론(:)을 주고, 공백을 하나 주고, 사용하고자 하는 타입의 이름을 주어서 타입 명시를 작성합니다.

다음 예제는 String 값을 저장할 수 있는 변수를 나타내기 위해, welcomeMessage라는 변수에 대한 타입 명시(type annotation)를 제공합니다.

var welcomeMessage: String

선언 안에 잇는 콜론(:)은 ~의 타입(of type)을 의미하므로, 위 코드는 다음과 같이 읽을 수 있습니다.

welcomeMessage라는 변수를 String 타입으로 선언합니다.

of type String 구문은 모든 String 값을 저장 할 수 있습니다를 의미합니다. the type of thing (또는 the kind of thing)은 저장될수 있는 것을 의미한다고 생각하세요.

welcomeMessage 변수는 이제 오류 없이 모든 문자열 값을 설정할 수 있습니다.

welcomeMessage = "Hello"

한 줄로 동일한 타입의 여러개의 관련된 변수들을 정의할 수 있고, 콤마(,)로 구분하고, 마지막 변수 이름 뒤에 하나의 타입 명시를 사용합니다.

var red, green, blue: Double

주의 실제로 타입 명시를 작성할 필요는 거의 없습니다. 상수나 변수가 정의되는 시점에 초기 값을 제공하는 경우, Swift는 Type Safety and Type Inference에서 설명된것처럼, 거의 항상 상수나 변수로 사용되는 타입을 추론할 수가 있습니다. 위 예제에 있는 welcomeMessage는, 초기 값을 제공하지 않았고, welcomeMessage 변수의 타입은 초기 값으로 추론되기보다는 타입 명시로 지정되었습니다.

상수와 변수 이름짓기(Naming Constatns and Variables)

상수와 변수 이름은 유니코드(Unicode) 문자를 포함해서, 거의 모든 문자를 포함할수 있습니다.

let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"

상수와 변수 이름은 공백문자(whitespace characters), 수학적 기호(mathematical symbols), 화살표(arrows), 개인적으로 사용하는 유니코드 스칼라 값, 선(line)과 박스(box) 그리는 문자들을 포함할 수 없습니다. 숫자를 이름의 다른곳에 포함 할 수는 있지만, 숫자로 시작할 수는 없습니다.

특정 타입으로 한번 선언된 상수와 변수는 같은 이름이나 다른 타입의 값을 저장하기 위해 바꿔서 다시 선언 할 수 없습니다. 또한 상수를 변수로 변경하거나 변수를 상수로 변경할 수 없습니다.

주의 상수나 변수를 예약된 Swift 키워드와 같은 이름이 필요한 경우에, 이름으로 사용할때 역따옴표(backticks `)로 키워드를 감싸줍니다. 하지만, 선택의 여지가 없는 경우를 제외하고 키워드를 이름으로 사용하는 것은 피하세요.

기존 변수의 값을 호환되는 다른 값의 타입으로 변경할수 있습니다. 예제에서, friendlyWelcom의 값은 "Hello!"에서 "Bonjour!"로 변경되었습니다.

var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome is now "Bonjour!"

변수와는 다르게, 상수는 설정된 이후에 변경할 수 없습니다. 이를 시도하는 경우 코드를 컴파일할때 오류가 발생됩니다.

let languageName = "Swift"
languageName = "Swift++"
// This is a compile-time error: languageName cannot be changed.

상수와 변수 출력하기(Printing Constants and Variables)

상수나 변수의 현재 값을 print(_:separator:terminator:) 함수로 출력할 수 있습니다.

print(friendlyWelcome)
// Prints "Bonjour!"

print(_:separator:terminator:) 함수는 하나 이상의 값을 적절하게 출력하는 전역 함수 입니다. 예를들어, Xcode에서, print(_:separator:terminator:) 함수는 Xcode의 콘솔(console) 영역에 출력합니다. separator와 terminator 매개변수는 기본 값을 가지고 있으므로, 함수를 호출할때 생략할 수 있습니다. 기본적으로, 그 함수는 줄 바꿈(line break)을 추가하여 출력을 마칩니다. 줄 바꿈(line break) 없이 출력하려면, terminator에 빈 문자열을 전달해야 합니다 - 예를 들어, print(someValue, terminator:""). 매개변수의 기본 값에 대한 정보는, Default Parameter Values를 보세요.

Swift는 긴 문자열에서 자리표시자(placeholder)처럼 변수나 상수의 이름을 포함하기 위해, 문자열 끼워넣기(string interpolation)을 사용하고, Swift는 상수나 변수의 현재값으로 교체합니다. 이름을 괄호(parentheses)로 감싸고 백슬러쉬(\)를 열린 괄호 앞에 붙여줍니다.

print("The current value of friendlyWelcome is \(friendlyWelcome)")
// Prints "The current value of friendlyWelcome is Bonjour!"

주의 문자열 끼워놓기로 사용할 수 있는 모든 옵션은 String Interpolation에 설명되어 있습니다.

주석(Comments)

스스로 주의하거나 기억해야 하는 것 처럼, 코드에서 실행할수 없는 텍스트를 포함하기 위해 주석을 사용합니다.

Swift에서의 주석은 C 언어에서의 주석과 매우 비슷합니다. 한 줄(single-line) 주석은 두개의 슬래쉬(//)로 시작합니다.

// This is a comment.

여러 줄(multiline) 주석은 슬래쉬와 별표(/*)로 시작하고 별표화 슬래쉬(*/)로 끝납니다.

/* This is also a comment
but is written over multiple lines. */

C 언어에서의 여러줄 주석과 다르게, Swift에서의 여러줄 주석은 다른 여러줄 주석을 안쪽으로 중첩될수 있습니다. 중첩된 주석을 작성합니다. 여러줄 주석 블럭을 시작하고 첫번째 블록안에 두번째 여러줄 주석을 시작합니다. 두번째 블록이 닫힌뒤에, 첫번째 블록이 옵니다.

/* This is the start of the first multiline comment.
 /* This is the second, nested multiline comment. */
This is the end of the first multiline comment. */

중첩된 여러줄 주석은 이미 여러줄 주석을 포함하고 있더라도 큰 블록을 쉽고 빠르게 주석처리할수 있습니다.

세미콜론(Semicolons)

많은 다른 언어들과는 다르게, Swift는 원한다면 할 수 있지만, 코드에서 각 문장 뒤에 세미콜론(;)을 작성하도록 요구하지 않습니다. 하지만, 한 줄(line)에서 여러개의 문장을 구분하길 원할때에는 세미콜론이 필요합니다.

let cat = "🐱"; print(cat)
// Prints "🐱"

정수(Integers)

정수(Integers)는 42와 -23과 같이 분수(factional) 구성요소가 없는 숫자입니다. 정수는 부호가 있는(signed)(양수, 0, 음수) 또는 부호가 없는(unsigned)(양수, 0) 둘 중 하나 입니다.

Swift는 8, 16, 32, 64비트 형식의 부호가 있고(signed) 부호가 없는(unsigned) 정수를 제공합니다. 이러한 정수들은 C 언어와 비슷한 명명규칙을 따르며, 8비트 부호 없는 정수는 UInt8 타입이고, 32비트 부호 있는 정수는 Int32 타입입니다. Swift에 있는 모든 타입과 같이, 이러한 정수 타입은 대문자로 된 이름을 가집니다.

정수 범위(Integer Bounds)

min과 max 프로퍼티(properties)로 각 정수 타입의 최소값과 최대값을 사용할 수 있습니다.

let minValue = UInt8.min  // minValue is equal to 0, and is of type UInt8
let maxValue = UInt8.max  // maxValue is equal to 255, and is of type UInt8

이 프로퍼티의 값들은 (위 예제에서의 UInt8과 같이)적절한 크기의 숫자 타입이고 표현식에서 동일한 타입의 다른 값과 나란히 사용할 수 있습니다.

Int

대부분의 경우, 코드에서 이를 사용하기 위해 정수의 크기를 지정할 필요가 없습니다. Swift는 현재 플랫폼의 기본 워드(word)크기와 동일한 크기를 가진 정수 타입 Int를 추가로 제공합니다.

  • 32비트 플랫폼에서, Int는 Int32와 동일한 크기입니다.
  • 64비트 플랫폼에서, Int는 Int64와 동일한 크기입니다.

특정 크기의 정수로 작업해야하는 경우가 아니라면, 코드에서 정수 값은 항상 Int를 사용합니다. 이는 코드 일관성(consistency)과 운영성(interoperability)에 도움이 됩니다. 32비트 플랫폼에서, Int는 -2,147,483,648과 2,147,483,647 사이의 모든 값을 저장할 수 있고 많은 정수를 저장하기에 충분히 큽니다.

UInt

또한 Swift는 현재 플랫폼의 기본 워드(word)와 동일한 크기의 부호없는 정수 타입 UInt를 제공합니다.

  • 32비트 플랫폼에서, UInt는 UInt32와 같은 크기입니다.
  • 64비트 플랫폼에서, UInt는 UInt64와 같은 크기입니다.

주의 플랫폼의 기본 워드 크기와 같은 크기의 부호없는 정수 타입이 필요할때만 UInt를 사용합니다. 그렇지 않은 경우에, 저장할 값이 음수가 아닌(nonnegative) 경우에도 Int를 사용(preferred)합니다. 정수 값에 대해 Int를 일관성 있게 사용하는 것은 Type Safety and Type Inference에서 설명된것처럼, 코드의 운용성(interoperability)을 도와주며, 다른 숫자 타입으로 변환할 필요가 없고, 정수 타입 추론을 일치시킵니다.

부동소수점 숫자(Floating-Point Numbers)

부동소수점 숫자(Floating-point numbers)는 3.14159, 0.1, -273.15와 같은 분수(fractional) 구성요소로 된 숫자입니다.

부동소수점 타입은 정수 타입보다 훨씬 더 넓은 범위의 값을 표현할 수 있고, Int에서 저장할수 있는 것보다 훨씬 더 크거나 더 작은 숫자들을 저장할 수 있습니다. Swift는 두개의 부호있는(signed) 부동소수점 숫자 타입을 제공합니다.

  • Double은 64비트 부동소수점 숫자를 표현
  • Float은 32비트 부동소수점 숫자를 표현

주의 Double은 최소 15자리의 정확도(precision)를 가지는 반면에, Float은 6자리의 정확도를 가질수 있습니다. 사용하는 적절한 부동소수점 타입은 코드에서 작업하는데 필요한 값의 특성과 범위에 따라 달라집니다. 두 타입 모두 사용가능한 경우에는 Double이 사용됩니다.

타입 안전성과 타입 추론(Type Safety and Type Inference)

Swift는 타입에 안전한(type-safe) 언어입니다. 타입에 안전한 언어는 코드에서 사용할 수 있는 값의 타입을 명확히 하는 것을 권장(encourages)합니다. 코드에서 String이 필요한 곳에, 실수로 Int를 전달할 수 없습니다.

Swift는 타입에 안전하기 때문에, 코드를 컴파일할때 타입 검사(type check)를 수행하고 일치하지 않는 타입을 오류로 표시합니다. 이는 개발 단계에서 가능한 빨리 오류를 잡고 고칠수 있는 것이 가능합니다.

타입 검사(Type-checking)하는 것은 다른 타입의 값으로 작업할때 오류를 피하는데 도움이 됩니다. 하지만, 이는 모든 상수와 변수의 타입을 지정해서 선언해야 하는 것을 의미하지 않습니다. 필요한 값의 타입을 지정하지 않은 경우에, Swift는 적절한 타입을 알아내기 위해 타입 추론(type inference)을 사용합니다. 타입 추론은 코드를 컴파일할때 단순히 제공한 값을 검사하는 것만으로도, 특정 표현식의 타입을 자동으로 추론하는 것이 가능합니다.

타입 추론(type inference) 때문에, Swift는 C나 Objective-C와 같은 다른 언어보다 타입 선언이 훨씬 더 적게 필요합니다. 상수와 변수는 여전히 명시적인으로 입력되지만, 이러한 타입을 지정하는 일을 해줍니다.

타입 추론은 초기 값으로 상수나 변수를 선언할때 특히 유용합니다. 이는 종종 상수나 변수를 선언하는 시점에 리터럴 값(literal value or literal)을 할당해서 처리합니다.(리터럴 값은 아래 예제의 42와 3.14159와 같이 코드에서 직접 표현하는 값입니다.)

예를들어, 42의 리터럴 값을 타입이 무엇인지 알리지 않고 새로운 상수로 할당하는 경우, Swift는 정수로 보이는 숫자로 초기화되었기 때문에, 상수가 원하는 것이 Int가 되도록 추론합니다.

let meaningOfLife = 42
// meaningOfLife is inferred to be of type Int

마찬가지로(Likewise), 부동소수점 리터럴에 대해 타입을 지정하지 않은 경우, Swift는 Double를 생성하길 원한다고 추론합니다.

let pi = 3.14159
// pi is inferred to be of type Double

Swift는 항상 부동 소수점 숫자를 추론할때 (Float대신에) Double을 선택합니다.

하나의 표현식에서 정수와 부동소수점 리터럴이 결합되어 있는 경우에는, 문맥상(context) Double 타입으로 추론될 것입니다.

let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double

리터럴 값 3은 자체적으로 명시된 타입이 없고, 따라서 Double의 적절한 출력 타입은 더하기 부분에서 부동소수점 리터럴의 존재에 의해 추론됩니다.

숫자 리터럴(Numeric Literals)

정수 리터럴은 다음과 같이 작성할 수 있습니다.

  • 10진수(decimal) 숫자는 접두사(prefix)없음
  • 2진수(binary) 숫자는 0b 접두사 사용
  • 8진수(octal) 숫자는 0o 접두사 사용
  • 16진수(hexadecimal) 숫자는 0x 접두사 사용

이러한 모든 정수 리터럴은 10진수 값 17입니다.

let decimalInteger = 17
let binaryInteger = 0b10001       // 17 in binary notation
let octalInteger = 0o21           // 17 in octal notation
let hexadecimalInteger = 0x11     // 17 in hexadecimal notation

부동소수점(floating-literals) 리터럴은 10진수(접두사 없음) 또는 16진수(접두사 0x)가 될 수 있습니다. 그것들은 언제나 소수점 양쪽에 숫자(또는 16진수 숫자)가 있어야 합니다. 10진수 부동소수점(floats)은 대문자 또는 소문자 e로 표시하는 지수(exponent)를 사용할 수 있습니다. 16진수 부동소수점은 대문자나 소문자 p로 표시하는 지수(exponent)가 있어야 합니다.

exp 지수(exponent)로 된 10진수 숫자의 경우에, 기본 숫자에 10^exp를 곱합니다.

  • 1.25e2는 1.25 x 102 또는 125.0을 의미
  • 1.25e-2는 1.25 x 10-2 또는 0.0125를 의미

exp 지수로 된 16진수 숫자의 경우에는 기본 숫자에 2^exp를 곱합니다.

  • 0xFp2는 15 x 22 또는 60.0을 의미
  • 0xFp-2는 15 x 2-2 또는 3.75를 의미

kka7 첨언

음수의 제곱승은 나누기로 표현됨. ex) 15 / (2 x 2) = 3.75

이러한 모든 부동소수점 리터럴은 12.1875의 10진수 값입니다.

let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0

숫자 리터럴은 읽기 쉽게하기 위해, 추가 서식(extra formatting)을 포함할 수 있습니다. 정수와 부동소수점 모두 읽기 쉽게 추가적으로 0을 채워넣을수 있고 밑줄(_)을 사용할 수 있습니다. 두 포멧 타입 모두 리터럴 기반의 값에서는 아무런 영향을 미치지 않습니다.

let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1

kka7 첨언

타입을 문자열로 확인해 보고 싶으면 변수 뒤에 .dynamicType를 붙인다.

숫자 타입 형변환(Numeric Type Conversion)

코드에서 음수가아닌 것을 알고 있더라도, 일반적인 경우(general-purpose)에 정수형 상수와 변수에 대해 Int 타입을 사용합니다. 일상적인 상황에서 기본 정수 타입을 사용하는 것은 정수형 상수와 변수를 코드에서 바로 사용할 수 있고, 정수 리터럴 값에 대해 추론된 타입과 일치할 것입니다.

외부 소스로부터 명시적인 크기의 데이터 또는 성능상, 메모리 사용, 또는 최적화가 필요한 경우, 특별히 다르게 처리해야할 필요가 있는 경우에만 다른 정수 타입을 사용합니다. 이러한 상황에서 명시적인 크기 타입을 사용하는 것은 값 오버플로우(overflow)가 발생하는 것을 잡고 사용되는 데이터의 특성을 암시적으로 문서화하는데 도움이 됩니다.

정수형 형변환(Integer Conversion)

정수형 상수나 변수에서 저장할수 있는 숫자의 범위는 각 숫자 타입에 따라 다릅니다. Int8 상수나 변수는 -128과 127 사이의 숫자들을 저장할수 있는 반면에, UInt8 상수나 변수는 0에서 255 사이의 숫자들을 저장할 수 있습니다. 크기가 지정된 정수 타입의 상수와 변수와 맞지 않은 숫자를 사용하는 경우에, 코드를 컴파일할때 오류가 발생합니다.

let cannotBeNegative: UInt8 = -1
// UInt8 cannot store negative numbers, and so this will report an error
let tooBig: Int8 = Int8.max + 1
// Int8 cannot store a number larger than its maximum value,
// and so this will also report an error

각 숫자 타입은 다른 범위의 값을 저장할 수 있기 때문에, 상황에 따라 형변환할 숫자 타입을 선택해야 합니다. 이런 선택적인 접근 방법은 숨겨진 형변환 오류를 막아주고 코드에서 타입 형변환 의도를 명시적으로 만드는데 도움이 됩니다.

특정 숫자 타입을 다른 타입으로 형변환하기 위해서, 기존 값으로 원하는 타입의 새로운 숫자를 초기화합니다. 아래 예제에서, 상수 twoThousand는 UInt16 타입인 반면에, 상수 one은 UInt8 타입입니다. 이것들은 동일한 타입이 아니기 때문에, 직접적으로 더하기(added) 할 수 없습니다. 그 대신에, 이 예제에서 one의 값으로 초기화되는 새로운 UInt16을 생성하기 위해 UInt16(one)를 호출하고, 원래 위치에서 이 값을 사용합니다.

let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)

더하기의 양쪽 모두 이제 UInt16 타입이기 때문에, 더하기가 가능합니다. 출력 상수(twoThousandAndOne)는 두개의 UInt16 값들의 합이기 때문에, UInt16 타입으로 추론됩니다.

SomeType(ofInitialValue)는 Swift의 타입 초기화를 호출하고 초기 값을 전달하는 기본적인 방법입니다. 내부적으로, UInt16은 UInt8 값을 받은 초기화(initializer)를 가지고, 이 초기화는 기존 UInt8로부터 새로운 UInt16을 만드는데 사용됩니다. 여기에서 모든(any) 타입을 전달 할 수 없습니다 - 하지만, UInt16이 초기화를 제공하는 타입이어야 합니다. 새로운 타입(자신만의 타입 정의를 포함해서)을 허용하는 초기화를 제공하기 위해서 기존 타입을 확장하는 것은 Extensions에서 다룹니다.

정수와 부동소수점 형변환(Integer and Floating-Point Conversion)

정수와 부동소수점 숫자 타입간의 형변환은 반드시 명시적으로 해야 합니다.

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double

여기에서, 상수 three의 값은 새로운 Double 타입의 값을 생성하는데 사용되므로, 더하기의 양쪽 모두 동일한 타입입니다. 이러한 형변환 없이는, 더하기는 허용되지 않습니다.

부동소수점을 정수형으로 형변환하는 것은 반드시 명시적으로 해야 합니다. 정수형 타입을 Double 또는 Float 값으로 초기화 할 수 있습니다.

let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int

부동소수점 값은 이와 같은 방식으로 새로운 정수형 값으로 초기화하기 위해 사용될때 항상 짤리게(truncated) 됩니다. 이는 4.75가 4가 되고, -3.9가 -3이 되는 것을 의미합니다.

주의 숫자 상수와 변수를 결합하는 규칙은 숫자 리터럴에 대한 규칙과는 다릅니다. 숫자 리터럴이 자체적으로 명시적인 타입을 가지고 있지 않기 때문에, 리터럴 값 3은 리터럴 값 0.14159에 직접 추가될수 있습니다. 이러한 타입은 컴파일러에 의해 처리되는 시점에 추론됩니다.

타입 별명(Type Aliases)

타입 별명(Type aliases)는 기존 타입에 대한 다른 이름을 정의합니다. typealias 키워드로 타입 별명을 정의합니다.

타입 별명(Type aliases)는 외부 소스로부터 특정 크기의 데이터로 작업할때처럼, 기존 타입을 문맥상 더 적절한 이름으로 참조하고자 할때 유용합니다.

typealias AudioSample = UInt16

타입 별명이 정의되고나면, 그 별명을 원래 이름을 사용할 수 있는 곳 어디서든 사용할 수 있습니다.

var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0

여기에서, AudioSample는 UInt16에 대한 별명으로 정의되었습니다. 이는 별명이기 때문에, AudioSample.min 호출은 실제로 UInt16.min을 호출하며, maxAmplitudeFound 변수에 대해 초기값 0을 제공합니다.

Booleans

Swift는 Bool 이라는 기본 Boolean 타입을 가지고 있습니다. Boolean 값은 true 또는 false만 될수 있기 때문에, 논리(logical) 값이라고 합니다. Swift는 두개의 Boolean 상수 값 true, false를 제공합니다.

let orangesAreOrange = true
let turnipsAreDelicious = false

orangesAreOrange와 turnipsAreDelicious의 타입은 Boolean 리터럴 값으로 초기화되었다는 사실로부터 Bool으로 추론됩니다. 위의 Int와 Double처럼, 생성하자마자 ture나 false를 설정하는 경우에, Bool로 상수나 변수를 선언할 필요가 없습니다. 타입 추론(Type inference)는 타입이 알려진 다른 값으로 상수나 변수를 초기화 할때, Swift 코드를 더 간결하고 읽기 쉽도록 도와줍니다.

Boolean 값은 if 문과 같은 조건문으로 작업할때 특히 유용합니다.

if turnipsAreDelicious {
    print("Mmm, tasty turnips!")
} else {
    print("Eww, turnips are horrible.")
}
// Prints "Eww, turnips are horrible."

if문과 같은 조건문은 Control Flow에서 더 자세히 다루게 됩니다.

Swift의 타입 안전성(type safety)은 Bool 대신에 Boolean이 아닌(non-Boolean) 값이 되는것을 막아줍니다. 다음 예제는 컴파일 오류가 발생합니다.

let i = 1
if i {
    // this example will not compile, and will report an error
}

하지만, 아래 다른 예제는 유효합니다.

let i = 1
if i == 1 {
    // this example will compile successfully
}

i == 1 비교 결과는 Bool 타입이고, 따라서, 두번째 예제에서 타입 검사를 통과합니다. i == 1같은 비교문은 Basic Operators에서 다루게됩니다.

Swift에서의 타입 안정성의 다른 예체처럼, 이러한 접근법은 실수로 발생하는 오류를 피하게해주고 코드의 특정 섹션의 의도를 항상 명확하게 보장해줍니다.

튜플(Tuples)

튜플(Tuples)은 여러개의 값을 단일 합성값으로 그룹화합니다. 튜플에 있는 값들은 어떤 ㅌ타입이든 가능하고 각각 같은 타입이 아니어도 됩니다.

예제에서, (404, "Not Found")는 HTTP 상태코드(HTTP status code)를 설명하는 튜플(tuple)입니다. HTTP 상태 코드는 웹페이지를 요청할때마다 웹서버로 부터 반환되는 특별한 값입니다. 404 Not Found 상태 코드는 요청한 웹 페이지가 존재하지 않은 경우에 반한됩니다.

let http404Error = (404, "Not Found")
// http404Error is of type (Int, String), and equals (404, "Not Found")

(404, "Not Found") 튜플 그룹은 HTTP 상태 코드를 두개의 값으로 구분해서 주기위해, Int와 String을 함게 사용합니다: 숫자와 사람이 읽을수 있는 설명. (Int, String) 타입의 튜플 처럼 설명될수 있습니다.

어떤 타입의 순서(any permutation of types)로 튜플을 생성할 수 있고, 많은 다른 타입을 포함할 수 있습니다. (Int, Int, Int) 또는 (String, Bool) 타입, 또는 원하는 다른 순서 타입의 튜플을 만들 수 있습니다.

튜플의 내용을 별도의 상수나 변수로 분해(decompose) 할 수 있으며, 그냥 사용하면 됩니다.

let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// Prints "The status code is 404"
print("The status message is \(statusMessage)")
// Prints "The status message is Not Found"

튜플의 값의 일부만 필요한 경우, 튜플을 분해할때 밑줄(_)로 튜플의 일부를 무시합니다.

let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// Prints "The status code is 404"

그렇지 않으면(Alternatively), 0으로 시작하는 인덱스 번호를 사용해서 튜플의 개별 요소의 값을 사용합니다.

print("The status code is \(http404Error.0)")
// Prints "The status code is 404"
print("The status message is \(http404Error.1)")
// Prints "The status message is Not Found"

튜플을 정의할때 튜플에 있는 개별 요소들의 이름을 사용할수 있습니다.

let http200Status = (statusCode: 200, description: "OK")

튜플에 있는 요소들에 이름이 있는 경우, 이러한 요소들의 값을 사용하기 위해 요소 이름을 사용할 수 있습니다.

print("The status code is \(http200Status.statusCode)")
// Prints "The status code is 200"
print("The status message is \(http200Status.description)")
// Prints "The status message is OK"

튜플은 함수의 반환 값으로 특히 유용합니다. 함수는 웹페이지 검색의 성공 또는 실패를 설명하기 위해 (Int, String) 튜플 타입을 반환합니다. 반환되는 튜플은 두개의 각기 다른 값으로 되어 있으며, 각각 다른 타입이며, 이 함수는 단일 타입의 단일 값 반환하는 것보다 더 유용한 정보를 제공합니다. 더 자세한 정보는 Functions with Multiple Return Values를 보세요.

주의 튜플은 관련된 값들을 임시로 그룹화하는데 유용합니다. 튜플은 복잡한 데이터 구조를 만들기에는 적합하지 않습니다. 데이터 구조를 임시로 사용하는 것이 아니라 지속적으로 사용하는 경우에, 튜플보다는 클래스나 구조체로 모델링 합니다. 더 자세한 정보는 Structures and Classes를 보세요.

옵셔널(Optionals)

옵셔널(optionals)은 값이 없을수도 있는 상황에서 사용합니다. 옵셔널은 두가지 가능성을 나타냅니다: 값이 있고 그 값을 사용하기 위해 옵셔널을 언래핑(unwrap)할수 있거나, 값이 전혀 없음 둘중 하나 입니다.

주의 옵셔널의 개념은 C 언어나 Objective-C에는 존재하지 않습니다. Objective-C에서 가장 비슷한 것으로는 메소드로부터 nil을 반환하거나 객체를 반환하는 것이며, nil은 유효한 객체가 없음을 의미합니다. 하지만, 이는 객체에 대해서만 동작합니다 - 구조체, 기본 C타입, 열거형 값들에 대해서는 동작하지 않습니다. 이러한 타입들에 대해서, Objective-C 메소드는 값이 없는 것을 가리키기 위해 일반적으로 특별한 값(NSNotFound)을 반환합니다. 이러한 접근법은 메소드의 호출자가 특별한 값을 알고 있어야 하는 것을 가정하며, 테스트하고 검사하는 것을 기억해야 합니다. Swift의 옵셔널은 특별한 상수가 없이도, 어떤 모든 타입(any type at all)의 값도 없음을 나타낼수 있습니다.

다음은 옵셔널에 값이 없을때 처리하는 방법에 대한 예제입니다. Swift의 Int 타입은 String값을 Int 값으로 변환하는 초기화를 가지고 있습니다. 하지만, 언제나 문자열이 정수로 형변환할 수 있는 것은 아닙니다. 문자열 "123"은 숫자 값 123으로 형변환 될수 있지만, 문자열 "hello, world"는 숫자 값으로 형변환하기 위한 분명한(obvious) 숫자값이 없습니다.

아래 예제는 String을 Int로 형변환하는 초기화를 사용합니다.

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?", or "optional Int"

초기화가 실패할수 있기 때문에, Int보다는 옵셔널(optional) Int를 반환합니다. 옵셔널 Int는 Int가 아니라 Int?로 작성됩니다. 물음표(?)는 값이 옵셔널을 포함하는 것을 가리키며, Int값을 포함할수 있거나, 아무 값도 가지지 않는(no value at all) 것을 의미합니다. (Bool 값이나 String값 같은 다른 것들을 포함할 수 없습니다. 그것은 Int이거나 아무것도 없는 둘중 하나입니다.)

nil

값이 없는 상태를 만들기위해 특별한 값 nil을 할당해서 옵셔널 변수를 설정합니다.

var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value

주의 옵셔널이 아닌 상수와 변수에 nil을 사용할수 없습니다. 코드에서 상수나 변수가 특정 조건에서 값이 없이 작업해야 하는 경우에, 항상 적절한 타입의 옵셔널 값으로 선언합니다.

기본 값을 제공하지 않고 옵셔널 변수를 정의하는 경우, 그 변수는 자동으로 nil을 설정합니다.

var surveyAnswer: String?
// surveyAnswer is automatically set to nil

주의 Swift의 nil은 Objective-C 에서의 inl과 같지 않습니다. Objective-C 에서의 nil은 존재하지 않는 객체에 대한 포인터입니다. Swift에서의 nil은 포인터가 아닙니다 - 특정 타입의 값이 없는 것을 의미합니다. 객체 타입뿐만아니라, 어떤(any) 타입의 옵셔널도 nil로 설정할 수 있습니다.

If문과 강제 언래핑(If Statements and Forced Unwrapping)

옵셔널이 nil인지 비교해서, 옵셔널에 값을 포함하고 있는지 확인하기 위해 if 문을 사용할 수 있습니다. ~와 같다(equal to) 연산자(==) 또는 ~와 같지않다(not equal to) 연산자(!=)를 사용해서 비교를 수행합니다.

옵셔널이 값을 가지고 있는 경우, nil과 같지 않다(not equal to)로 간주합니다.

if convertedNumber != nil {
    print("convertedNumber contains some integer value.")
}
// Prints "convertedNumber contains some integer value."

옵셔널에 값이 있다고 확신하는 경우, 옵셔널의 이름 끝에 느낌표(!)를 추가해서 값을 사용할수 있습니다. 느낌표(!)는 옵셔널이 확실히 값을 가지고 있는 것을 알고있으며, 이를 사용합니다.라는 것을 의미합니다. 이는 옵셔널 값의 강제 언래핑(forced unwrapping)이라고 합니다.

if convertedNumber != nil {
    print("convertedNumber has an integer value of \(convertedNumber!).")
}
// Prints "convertedNumber has an integer value of 123."

if문에 대한 자세한 것은 Control Flow를 보세요.

주의 옵셔널 값이 존재하지 않을때 !를 사용하려고 하면 런타임 오류가 발생합니다. 값을 강제 언래핑하기 위해 !를 사용하기 전에, 옵셔널에 nil이 아닌 값을 포함하고 있는지 확인해야 합니다.

옵셔널 바인딩(Optional Binding)

옵셔널에 값을 가지고 있는지 알기위해 옵셔널 바인딩(optional binding)을 사용하고, 만약 있다면, 그 값을 임시 상수나 변수로 사용할 수 있습니다. 옵셔널 바인딩은 옵셔널 안에 값을 확인하기 위해, if와 while문에서 사용될 수 있고, 단일 동작으로 그 값을 상수나 변수로 추출합니다. if와 while문은 Control Flow에서 더 자세히 설명되어 있습니다.

if문에 대한 옵셔널 바인딩을 다음과 같이 작성합니다.

if let constantName = someOptional {
    statements
}

Optionals 섹션의 possibleNumber 예제를 강제 언래핑 대신에 옵셔널 바인딩을 사용하도록 다시작성할 수 있습니다.

if let actualNumber = Int(possibleNumber) {
    print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {
    print("The string \"\(possibleNumber)\" could not be converted to an integer")
}
// Prints "The string "123" has an integer value of 123"

이 코드는 다음과 같이 읽을 수 있습니다.

Int(possibleNumber)으로 반환된 옵셔널 Int가 값을 포함하고 있는 경우, 옵셔널에 포함된 값을 새로운 상수 actualNumber에 설정합니다.

변환이 성공한 경우, actualNumber 상수는 if문의 첫번째 분기(branch)안에서 사용할 수 있습니다. 그것은 이미 옵셔널 안에 포함된 값으로 초기화되었고, 값을 사용하기 위해서 !를 접미사로 붙일필요가 없습니다. 이 예제에서, actualNumber는 단순히 변환의 결과를 출력하기 위해 사용됩니다.

포함된 값을 사용한(accessing) 후에, 원본의 옵셔널 상수 또는 변수를 참조할 필요가 없는 경우에, 새로운 상수나 변수에 같은 이름을 사용할 수 있습니다.

let myNumber = Int(possibleNumber)
// Here, myNumber is an optional integer
if let myNumber = myNumber {
    // Here, myNumber is a non-optional integer
    print("My number is \(myNumber)")
}
// Prints "My number is 123"

이 코드는 이전 예제의 코드처럼, myNumber에 값을 포함되어 있는지 확인하는 것으로 시작합니다. myNumber는 값을 가지며, 그 값의 새로운 상수는 myNamer이름으로 해당 값을 설정합니다. if문의 본문 안쪽에서, myNumber은 새로운 옵셔널이 아닌(non-optional) 상수를 나타냅니다. if문의 시작 이전과, 끝 이후에 작성한 myNumber은 옵셔널 정수 상수를 나타냅니다.

이런 종류의 코드가 너무 흔하기에, 더 짧게 사용해서 옵셔널 값을 언래핑(unwrpa) 할 수 있습니다: 언래핑할 상수나 변수의 이름만 사용하세요. 새로 언래핑된 상수나 변수는 암시적으로 옵셔널 값과 같은 이름을 사용합니다.

if let myNumber {
    print("My number is \(myNumber)")
}
// Prints "My number is 123"

옵셔널 바인딩에 상수와 변수 모두 사용할 수 있습니다. if문의 첫번째 분기(branch) 안에서 myNumber의 값을 처리하기(manipulate) 원하는 경우, if var myNumber를 작성할 수 있고 그 값은 옵셔널에 포함된 값은 상수가 아닌 변수로 사용가능합니다. if문의 본문에 있는 myNumber에 대한 변경은 해당 지역변수에만 적용되고, 언래핑된 원본, 옵셔널 상수 또는 언래핑된 변수에는 적용되지 않습니다.

필요한 경우 하나의 if문 안에서 여러개의 옵셔널 바인딩과 Boolean 조건문을 콤마(,)로 구분해서 포함할 수 있습니다. 옵셔널 바인딩에는 모든 값이 nil이거나 모든 Boolean 조건이 false가 되는 경우, if문의 조건은 false로 처리됩니다. 다음에 오는 if문은 동일합니다.

if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
    print("\(firstNumber) < \(secondNumber) < 100")
}
// Prints "4 < 42 < 100"

if let firstNumber = Int("4") {
    if let secondNumber = Int("42") {
        if firstNumber < secondNumber && secondNumber < 100 {
            print("\(firstNumber) < \(secondNumber) < 100")
        }
    }
}
// Prints "4 < 42 < 100"

주의 if문 안에서 옵셔널 바인딩으로 생성된 상수와 변수는 if문 본문에서만 사용가능합니다. 반면에, guard문으로 생성된 상수와 변수는 guard 문 아래의 코드에서만 사용가능하며, Early Exit에서 설명되어 있습니다.

암시적으로 언래핑된 옵셔널(Implicitly Unwrapped Optionals)

위에서 설명한것 처럼, 옵셔널은 상수나 변수가 값 없음을 허용되는 것을 가리킵니다. 옵셔널은 값이 존재하는지 보기 위해서 if문으로 검사할수 있고, 옵셔널의 값이 존재하는 경우 사용하기 위해 옵셔널 바인딩으로 조건부로 언래핑할수 있습니다.

프로그램의 구조상으로 명확하게 옵셔널이 처음 설정된 이후에 항상(always) 값을 가지고 있어야 할때가 있습니다. 이 경우에, 항상 안전하게 값을 가지고 있는 것을 가정할수 있기 때문에, 옵셔널 값을 검사하고 언래핑하는 것을 제거하는것이 유용합니다.

이러한 종류의 옵셔널을 암시적으로 언래핑된 옵셔널(implicitly unwrapped optionals)로 정의됩니다. 옵셔널로 만들고자하는 타입의 뒤에 물음표(String?) 대신에 느낌표(String!)를 붙여서 암시적으로 언래핑된 옵셔널을 작성합니다.

암시적으로 언래핑된 옵셔널은 옵셔널 값이 처음 정의된 직후에 존재하는 것이 확인되고 그 이후에 모든 시점에 반드시 존재한다고 가성할수 있을때 유용합니다. Swift에서 암시적으로 언래핑된 옵셔널의 주된 용도는 클래스 초기화하는데 이며, Unowned References and Implicitly Unwrapped Optional Properties에서 설명되어 있습니다.

암시적으로 언래핑된 옵셔널은 일반 옵셔널 바인딩이지만, 사용할때마다 옵셔널을 언래핑 할 필요없이, 옵셔널이 아닌 값처럼 사용될 수 있습니다. 다음에 오는 예제는 명시적인 String으로 래핑된 값을 사용하고자 할때, 옵셔널 문자열과 암시적으로 언래핑된 옵셔널 문자열간의 차이점을 보여줍니다.

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation mark

암시적으로 언래핑된 옵셔널이 사용될때마다 자동으로 언래핑되도록하기 위해 옵셔널에 대한 권한을 주는 것으로 생각할 수 있습니다. 매번 옵셔널의 이름 뒤에 느낌표(!)를 사용하는 대신에, 선언할때 옵셔널 타입 뒤에 느낌표(!)를 붙입니다.

주의 암시적으로 언래핑된 옵셔널이 nil이고 래핑된 값을 사용하려고 하는 경우, 런타임 오류가 발생할 것입니다. 이 결과는 값을 포함하지 않은 일반 옵셔널 뒤에 느낌표(!)를 붙이는 것과 똑같습니다.

암시적으로 언래핑된 옵셔널을 값이 포함되었는지 검사하기 위해, 일반 옵셔널처럼 처리할 수 있습니다.

if assumedString != nil {
    print(assumedString!)
}
// Prints "An implicitly unwrapped optional string."

또한 단일 구문으로 검사하고 값을 언래핑하기 위해, 옵셔널 바인딩으로 암시적으로 언래핑된 옵셔널을 사용할 수 있습니다.

if let definiteString = assumedString {
    print(definiteString)
}
// Prints "An implicitly unwrapped optional string."

주의 나중에라도 nil이 될수 있는 가능성이 있을때는 암시적으로 언래핑된 옵셔널을 사용하지 마세요. 변수를 사용하는 동안 nil 값을 검사할 필요가 있는 경우에, 항상 일반 옵셔널 타입을 사용하세요.

오류 처리(Error Handling)

프로그램이 실행되는 동안 발생할 수 있는 오류 조건을 처리하기 위해 오류 처리(error handling)을 사용합니다.

옵셔널과는 다르게, 함수의 성공이나 실패를 전달하기 위해 값의 유무를 확인할 수 있으며, 오류 처리를 통해 오류의 근본적인 원인을 판별하고(determine), 필요한 경우, 프로그램의 다른 부분으로 오류를 전달합니다.

함수가 오류 상태가 될때, 오류를 던집니다(throws). 함수의 호출자는 오류를 잡을 수(catch) 있고 적절히 처리할 수 있습니다.

func canThrowAnError() throws {
    // this function may or may not throw an error
}

함수는 선언에서 throws키워드를 포함함으로써 오류를 던질수 있다는 것을 가리킵니다. 오류를 던질수 있는 함수를 호출할때, try 키워드를 그 표현식의 앞에 붙입니다(prepend).

Swift는 자동적으로 catch절에 의해서 처리될때까지 현재 범위의 바깥으로 오류를 전달합니다.

do {
    try canThrowAnError()
    // no error was thrown
} catch {
    // an error was thrown
}

do구문은 하나 이상의 catch절에 오류를 전달할수 있는 새로운 포함 범위를 만듭니다.

다음은 다른 오류 조건을 처리하기 위해 사용될수 있는 오류 처리 방법의 예제입니다.

func makeASandwich() throws {
    // ...
}

do {
    try makeASandwich()
    eatASandwich()
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)
}

이 예제에서, makeASandwich() 함수는 깨끗한 접시가 없거나 재료가 빠진 경우에 오류를 던질 것입니다. makeASandwich()는 오류를 던질수 있기 때문에, 함수 호출은 try 표현식으로 감싸져야 합니다. do 구문에서 함수를 래핑함으로써, 모든 오류는 제공된 catch절에 전달될것입니다.

오류가 없는 경우, eatASandwich() 함수가 호출됩니다. SendwichError.outOfCleanDishes와 일치하는 오류가 발생하는 경우, washDishes() 함수가 호출될것입니다. SandwichError.missingIngredients와 일치하는 오류가 발생하는 경우, catch 패턴에 의해 캡쳐된 연관된 [String] 값으로 buyGroceries(_:) 함수가 호출됩니다.

오류 던지기(Throwing), 잡기(catching), 전달하기(propagating)은 Error Handling에서 더 자세히 다루게됩니다.

단언하기와 필수조건(Assertions and Preconditions)

단언하기(Assertion)와 필수조건(preconditions)는 런타임으로 발생하는 것을 검사합니다. 추가 코드를 실행하기 전에 필요한 조건을 만족하는지 확인하기 위해 사용합니다. 단언하기(Assertion)와 필수조건(preconditions)의 Boolean 조건이 true가 되는 경우, 평상시처럼, 코드는 계속 실행됩니다. 조건이 false가 되는 경우, 프로그램의 현재 상태는 유효하지 않게 됩니다; 코드 실행은 끝나고 앱은 종료됩니다.

코딩하는 동안 가정한것(assumptions)과 기대한것(expectations)을 표현하기 위해서 단언하기(assertion)와 필수조건(preconditions)을 사용하며, 코드의 일부처럼 포함할 수 있습니다. 단언하기(Assertions)는 개발하는 도중에 실수한 것과 잘못 가정한 것을 찾는데 도움이 되고, 필수조건(preconditions)는 제품(production)에서 문제를 발견하는데 도움이 됩니다.

런타입에, 기대한 것을 확인하는것 외에도, 단정하기(assertions)와 필수조건(preconditions)는 코드내에서 유용한 문서 형식이 됩니다. 위의 [Error Handling]에서 논의했던 오류 조건과는 다르게, 단정하기(assertions)와 필수조건(preconditions)는 복구하거나 예상된 오류에 대해 사용되지 않습니다. 실패한 단정하기(assertions)와 필수조건(preconditions)는 잘못된 프로그램 상태를 가리키기 때문에, 실패한 단정하기(assertion)를 잡을수 있는 방법은 없습니다.

단정하기(assertions)와 필수조건(preconditions)은 유효하지 않는 조건이 발생하지 않도록 코드를 설계하는데에 사용할 수 없습니다. 하지만, 유효하지 않은 상태가 발생한 경우에, 유효한 데이터가 되도록하고 앱이 예측 가능하게 종료하도록 할때 사용하고, 문제에 대해 디버깅을 쉽게하도록 도와줍니다. 유효하지 않는 상태가 감지되는 즉시 실행을 멈추는 것은 유효하지 않은 상태로 인해 발생되는 손상을 최소화하는데 도움이 됩니다.

단정하기(assertions)와 필수조건(precondition)간의 차이점은 그것들을 검사할때에 있습니다: 단정하기(Assertions)는 디버깅 빌드에서만 검사되지만, 필수조건(precondition)은 디버깅과 제품 빌드 모두에서 검사됩니다. 제품 빌드에서, 단정하기(Assertions) 안쪽의 조건은 처리되지 않습니다. 이는 개발 단계에서 원하는 만큼의 많은 단정하기(assertions)을 사용할 수있는 것을 의미하며, 제품의 성능에 영향을 미치지 않습니다.

단정하기로 디버깅하기(Debugging with Assertions)

Swift 표준 라이브러리로부터 assert(::file:line:) 함수를 호출해서 단정하기(assertion)를 작성합니다. 이 함수에 true 또는 false를 처리하는 표현식을 전달하고 그 조건식의 결과가 false인 경우에 보여주기 위한 메시지(message)를 전달합니다. 예를 들어:

let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 is not >= 0.

이 예제에서, if age > = 0이 true가 되면 코드 실행은 계속되며, 이는 age의 값이 음수가 아닌경우입니다. age의 값이 음수인경우, 위 코드에서 처럼, age >= 0은 false로 처리되고, 단정하기(assertion)는 실패하며, 앱은 종료합니다.

단정하기 메시지를 생략할 수 있습니다 - 예를들어, 조건이 평범하게(prose) 반복될때입니다.

assert(age >= 0)

조건을 이미 확인한 코드의 경우에, 단정하기(assertion)가 실패되는 것을 나타내는 assertionFailure(_:file:line:)함수를 사용합니다. 예를들어:

if age > 10 {
    print("You can ride the roller-coaster or the ferris wheel.")
} else if age >= 0 {
    print("You can ride the ferris wheel.")
} else {
    assertionFailure("A person's age can't be less than zero.")
}

필수조건 적용하기(Enforcing Preconditions)

조건이 false될 가능성이 있지만, 코드를 계속 실행하기 위해서는 명확히(definitely) true가 되어야 하는 경우에, 필수조건(precondition)을 사용합니다. 예를들어, 서브스크림트가 범위를 벗어나지 않았는지 검사하기 위해 또는 함수에 유효한 값이 전달되었는지 확인하기 위해 필수조건(precondition)을 사용합니다.

필수조건(precondition)을 precondition(::file:line)함수를 호출해서 작성합니다. 함수에 true또는 false로 처리하는 표현식을 전달하고 조건의 결과가 false인 경우에 보여주기 위한 메시지를 전달합니다. 예를 들어:

// In the implementation of a subscript...
precondition(index > 0, "Index must be greater than zero.")

또한 실패가 발생했음을 가리키기 위해 preconditionFailure(_:file:line:) 함수를 호출할수 있습니다 - 예를들어, switch문의 default case가 있지만, 모든 유효한 입력 데이터를 switch의 다른 case들 중 하나로 처리해야 합니다.

주의 검사하지 않는 모드(-Ounchecked)에서 컴파일하는 경우, 필수조건(preconditions)은 검사되지 않습니다. 컴파일러는 필수조건(preconditions)이 항상 true로 가정하고, 코드에 따라 적절하게 최적화합니다. 하지만, fatalError(_:file:line) 함수는 최적화 설정과는 무관하게, 항상 실행을 멈춥니다.

프로토타입(prototyping)과 아직 구현되지 않은 기능을 개발하기 위해서 fatalError(_:line:) 함수를 사용할 수 있으며, 구현할 부분에 fatalError("Unimplemented")을 작성합니다. 치명적인(fatal) 오류(error)는 결코 최적화되지 않기 때문에, 단정하기(assertions)나 필수조건(preconditions)과는 다르며, 구현된 부분(stup implementation)을 만나면 항상 실행이 중단되는 것을 확신할 수 있습니다.

반응형

'Swift > Language Guide' 카테고리의 다른 글

흐름제어(Control Flow)  (0) 2022.06.21
동시성(Concurrency)  (0) 2022.06.21
불분명한 타입(Opaque Types)  (0) 2019.12.12
컬렉션 타입(Collection Types)  (0) 2019.03.05
문자열과 문자(Strings and Characters)  (0) 2019.02.28
기본 연산자(Basic Operators)  (0) 2019.02.26
Advanced Operators  (0) 2018.09.18
Access Control  (0) 2018.09.18
Posted by 까칠코더
,