 Apple Lover Developer & Artist

영속적인 디자인에 현대의 공감을 채워넣는 공방입니다

 Apple/Swift Programming Language

[Swift] 공식문서 씹어먹기: The Basics

singularis7 2021. 9. 19. 15:27
반응형

Swift Logo

Overview

  • Swift는 iOS, macOS, watchOS, tvOS의 어플리케이션 개발을 위한 새로운 프로그래밍 언어이며 C와 Objectice-C 개발 경험이 있다면 Swift를 쉽게 활용할 수 있다.
  • Swift는 C와 Objective-C에서의 타입과 콜렉션에 대해여 자신만의 버전을 갖고 있다.

 

Int Integer
Double, Float Floating-point values
Bool Boolean
String Texture data
Array, Set, Dictionary Collection type

 

  • C와 같이 Swift는 변수를 이름으로 식별하여 값을 저장(store)하고 참조(refer)하는 용도로 사용한다.
  • C에서의 상수(constant) 개념처럼 Swift에서도 값이 변할 수 없는 변수에 대한 개념이 있다.
  • 상수(constant)는 변하지 않는 값을 다룰때 의도(intent)를 분명하게하고 코드를 안전하게(safer) 만들어준다.

 

  • Objective-C에서 찾을 수 없는 tuple과 같은 자료형이 존재한다.
  • 값들의 모임(grouping of values)을 생성(create)하고 넘겨(pass)주는데 활용할 수 있다.
  • 함수에서 여러가지 값들을 리턴해야 할때 응집된 하나의 값으로 만드는데 유용하게 활용할 수 있다.

 

  • Swift는 값의 부재(absence)를 다룰 수 있는 optional types이 있다.
  • Optional은 "값이 존재하며 그 값은 x이다" 혹은 "값이 들어있지않다"를 표현할 수 있다.
  • Objective-C에서 nil 포인터와 유사하지만 class가 아니라 any type의 형태로 동작한다.
  • Optional은 Objective-C에서의 nil 포인터 방식보다 더 안전하고 표현적(expressive)인 방법이며 다수의 Swift의 가장 강력한 기능의 핵심이다.

 

  • Swift는 type-safe 언어이며 당신이 작업하는 코드의 type을 더 명확하게 할 수 있도록 도와준다.
  • 예를 들어 현재 작업하는 코드가 String을 요구하는데 Int를 넘겨주면 type-safety가 막아준다.
  • 마찬가지로 옵셔널 타입의 String을 요구하는데 비옵셔널 타입의 String을 넘겨주는 상황도 막아준다.
  • Type-Safety는 개발 단계에서 오류를 잡을 수 있도록 도와준다.

Constants and Variables

  • 상수(Constants)와 변수(Variables)는 특정 type의 값(value)과 함께하는 name과 연관되어있다.
  • 변수이름의 예시: maximumNumberOfLoginAttempts, welcomeMessage
  • 특정 type을 갖는 값(value)의 예시: 숫자 10, string "Hello"
  • 상수(Constant)는 한번 값이 설정되면 변동될 수 없다.
  • 변수(Variable)는 한번 값이 설정된 이후에도 다른 값으로 설정될 수 있다.

 

Declaring Constants and Variables

  • 상수(Constants)와 변수(Variables)는 반드시 사용되기 전에 선언되어야 한다.
  • 상수(Constants)는 let 키워드를 통해 선언할 수 있다.
  • 변수(Variables)는 var 키워드를 통해 선언할 수 있다.
  • 다음은 사용자가 시도한 로그인 횟수를 추적하는 코드의 예시이다.
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
  • maximumNumberOfLoginAttempts로 불리는 새로운 상수를 선언하여 값 10을 할당했다.
  • currentLoginAttempt로 불리는 새로운 변수를 선언하여 초기값 0을 할당했다.
  • 최대 로그인 횟수는 변하지 않기 때문에 상수로 선언되었으며 현재 로그인 시도 횟수는 로그인 시도가 실패할 때마다 증가 되기 때문에 변수로 선언되었다.
  • 여러개의 상수 혹은 변수를 선언할 때 콤마(,)로 구분하여 한번에 한줄로 선언할 수 있다.
var x = 0.0, y = 0.0, z = 0.0
Note
만약 당신의 코드에서 저장된 값이 변하지 않는 경우 항상 let 키워드를 사용하여 상수로 선언해주세요. 변수는 오직 변동될 필요가 있는 수를 저장하는 용도로 사용해주세요.

 

Type Annotations

  • type annotation은 상수나 변수를 선언할 때 저장될 값의 종류를 명확하게하는 용도로 사용할 수 있다.
  • type annotation을 선언하는 방법은 다음과 같다.
var welcomeMessage: String
  • welcomeMessage로 불리는 변수를 선언했으며 변수의 type은 String이다.
  • 변수의 type이 String이라는 의미는 어떠한 String 값도 저장될 수 있다는 의미이다.
welcomeMessage = "Hello"
  • 동일한 타입을 갖는 여러개의 변수를 다음과 같은 방법으로 선언할 수 있다.
var red, green, blue: Double
Note
사전에 당신이 type annotation을 명시해주는 일이 흔하지는 않습니다. 상수나 변수에 초기값을 할당하면 Swift가 해당 상수나 변수에 알맞은 type을 추론(infer)하기 때문입니다. 자세한 사항은 Type Safety and Type Inference 를 확인해주세요!

 

Naming Constants and Variables

  • 상수나 변수의 이름은 Unicode 문자들을 포함한 대부분의 문자를 포함할 수 있다.
let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"
  • 상수나 변수의 이름에 포함될 수 없는 것들이 존재한다.
  • 공백문자, 수학적 기호, 화살표, private-use Unicode 스칼라 값, 선, 박스가 그려진 문자들
  • 변수 이름에 숫자가 포함될 수 있지만 이름의 시작이 숫자이면 안된다.
  • 어느 이름으로 상수나 변수가 생성되면 다음과 같은 행위를 할 수 없다.
  • 같은 이름으로 다른 상수나 변수를 선언하는 행위
  • 선언된 변수의 타입을 변경하는 행위
  • 상수를 변수로 혹은 변수를 상수로 변경하는 행위
Note
상수나 변수의 이름을 Swift에서 제공하는 예약어 키워드로 지정할 필요가 있다면 backticks (`) 를 함께 사용하여 지정할 수 있지만 권장하지 않습니다.

 

  • 이미 선언된 변수의 값을 호환되는 타입의 다른 값으로 바꾸기 위해 다음과 같은 방법을 사용할 수 있다.
var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome is now "Bonjour!"
  • 변수와는 다르게 상수는 값이 한번 설정되고난 후에 값을 변경하려고 하면 컴파일 에러가 발생한다.
let languageName = "Swift"
languageName = "Swift++"
// This is a compile-time error: languageName can't be changed.

 

Printing Constants and Variables

  • 상수 혹은 변수의 값을 출력하려면 print(_:separator:terminator:) 함수를 사용하면 된다.
print(friendlyWelcome)
// Prints "Bonjour!"
  • print(_:separator:terminator:) 함수는 global 함수이며 하나 이상의 값에 대해 Xcode의 콘솔창에 출력시켜준다.
  • separator, terminator 파라미터는 default 값이 지정되어있어서 line break이 포함된 상태로 출력되는 것이 기본값이다.
  • line break 없이 값을 출력하려면 print(soneValue, terminator: "") 방식을 사용할 수 있다.
  • string interpolation 기법을 사용하여 긴 string 문장에서 상수나 변수를 placeholder로 사용할 수 있다.
print("The current value of friendlyWelcome is \(friendlyWelcome)")
// Prints "The current value of friendlyWelcome is Bonjour!"
Note
String Interpolation 문서를 참조하면 당신이 사용할 수 있는 모든 option을 살펴볼 수 있습니다.

Comments

  • 주석을 활용하여 코드에 실행되지 않는 기록하거나 remind 할 수 있는 각종 텍스트를 포함시킬 수 있다.
  • C에서 사용되는 주석 사용 방법("//")과 유사하다.
// This is a comment.
  • Multiline comments를 사용하여 시작("/*")과 끝("*/")을 표현해주면 여러 줄에 대한 주석 표시가 가능하다
/* This is also a comment
but is written over multiple lines. */
  • Multiline comments을 다른 Multiline comments에 포함되도록 사용할 수 있다.
/* 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. */
  • Multiline comments는 큰 블록의 코드에 이미 주석이 포함되었을지라도 쉽고 빠르게 주석처리하는데 도움이 된다.

Semicolons

  • 다수의 다른 언어와 다르게 Swift에서는 세미콜론(;)을 사용하는 것을 요구하지 않는다.
  • 사용하고 싶다면 사용해도 무방하다
  • 여러 코드를 한줄에 입력해야 할 때에는 반드시 세미콜론을 사용해야 한다.
let cat = "🐱"; print(cat)
// Prints "🐱"

Integer

  • 정수(Integer)는 분수(fractional component)가 아닌 모든 수이다. 예를 들어 42, -23이 있다.
  • 정수에는 signed(양수, 0, 음수) 혹은 unsigned(양수, 0)이 있다.
  • Swift는 8, 16, 32, 64비트형의 signed와 unsigned 정수(integer)를 제공한다.
  • 각 자료형의 naming convention은 C와 유사하다.
  • 8-bit unsigned integer => UInt8, 32-bit signed integer => Int32
  • Swift의 다른 자료형에서도 이와 유사하게 capitalized name을 사용한다.

 

Integer Bounds

  • 각 Integer type이 제공할 수 있는 최대 / 최소값에 접근하기 위해 해당 type의 min, max 프로퍼티를 사용할 수 있다.
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

 

Int

  • 일반적인 경우 코드에서 Int를 사용할 때 구체적인 크기를 지정해줄가 없다.
  • Swift는 추가적인 Int 타입을 제공하며 현재 플랫폼의 native world size와 동일한 size를 갖도록 동작한다.
  • Int는 32비트 플랫폼에서 Int32, 64비트 플랫폼에서 Int64와 동일한 크기를 갖는다.
  • 위와 같이 구체적인 크기를 갖는 Int를 선언할 수 있지만 코드에서는 항상 Int를 사용해야한다.
  • Int를 사용하면 코드의 consistency 및 interoperability 부분에 대한 지원을 해준다.

 

UInt

  • Swift는 unsigned integer type인 UInt를 제공하며 크기는 현재 플랫폼의 native word size와 동일하다.
Note
UInt는 플랫폼의 native word size와 동일한 크기를 갖는 unsigned integer type이 필툐한 특정 상황에 사용하세요. 이 경우가 아니라면 Int를 사용하는 것이 선호됩니다. 아무리 값에 대해 음수가 사용되지 않을 지라도 정수값에 대하여 지속적으로 Int를 사용하는 것이 interoperability 지원을 받고, type 변환을 피하며, integer type 추론을 맞추는데 좋습니다.

Floating-Point Numbers

  • 부동 소수점 수는 분수(fractional component)를 갖는 수이다. 예를 들어 3.14159, 0.1, -273.15가 있다.
  • 정수(integer) 타입보다 더 넓은 범위의 수를 표현할 수 있으며 정수보다 훨씬 큰 수 혹은 작은 수를 저장할 수 있다.
  • Swift는 다음과 같은 두가지의 signed floating-point 수를 제공한다.
  • Double은 64-bit 부동소수점 수를 표현한다.
  • Float은 32-bit 부동소수점 수를 표현한다.
Note
Double은 적어도 15 decimal digits의 정확도를 갖지만 Float은 6 decimal digits의 정확도 밖에 갖지 못합니다. Floating Point Type을 사용할 때에는 상황에 따라 적절한 타입을 선택해야 합니다. 두가지 타입 모두 적절하다고 판단된다면 Double을 더 선호합니다.

Type Safety and Type Inference

  • Swift는 type-safe 언어이다. 코드가 동작할 수 있는 값의 type을 명확히 알 수 있다.
  • Swift가 type-safe 하기 때문에 코드를 컴파일할 때 type 체크를 수행한다.
  • 만약 알맞지 않은 type이 존재한다면 오류를 발생시켜 개발단계에서 오류를 수정할 수 있도록 한다.

 

  • type-checking은 서로 다른 type의 값을 다룰 때 발생될 수 있는 error를 예방하는데 도움이 된다.
  • 그렇다고 모든 상수나 변수를 선언할 때 타입을 명시하라는 의미는 아니다.
  • 개발자가 type을 명시하지 않으면 Swift가 type inference 기능을 통해 적절한 타입을 추론한다.
  • 컴파일러가 당신이 제공한 값에 기반하여 컴파일 시에 적절한 type을 추론해 주는 것이다.

 

  • type inference 기능을 통해 C 혹은 Objective-C와 다르게 훨씬 더 적은 type 선언을 요구한다.
  • 상수 혹은 변수는 여전히 명시적으로 type되어 있으며 type을 명시해주는 대다수의 작업들이 당신을 위해 처리되어있다!
  • type inference 기능은 특히 초기값이 지정된 상수 혹은 변수를 선언할 때 유용하다.
  • 변수 혹은 상수를 선언한 시점에서 literal 값을 할당함으로써 type inference가 이뤄진다.
  • literal 값이란 42, 3.14159 와 같이 소스 코드에서 직접적으로 등장하는 값을 의미한다.
let meaningOfLife = 42
// meaningOfLife is inferred to be of type Int

let pi = 3.14159
// pi is inffered to be of type Int

let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double
  • Swift에서는 42와 같이 Integer 처럼 보이는 literal 값의 type을 Int로 추론한다.
  • 3.14159와 같이 Floating-point number 처럼 보이는 literal 값의 type을 Double로 추론한다.
  • 정수값과 부동소수점 수값의 합으로 literal 값이 주어지면 type을 Double로 추론한다.

Numeric Literals

  • 정수형 literal 값은 다음과 같은 방식으로 표현될 수 있다.
  • 아무런 접두사를 붙이지 않으면 10진수를 따른다.
  • 0b 접두사를 붙이면 2진수를 따른다.
  • 0o 접두사를 붙이면 8진수를 따른다.
  • 0x 접두사를 붙이면 16진수를 따른다.
let decimalInteger = 17
let binaryInteger = 0b10001 	// 17 in binary notation
let octalInteger = 0o21			// 17 in octal notation
let hexadecimalInteger = 0x11	// 17 in hexadecimal notation
  • 부동소수점형 literal 값은 10진수(접두사 없음) 혹은 16진수(0x접두사)의 형태로 선언될 수 있다.
  • 10진수의 지수부는 e,  16진수의 지수부는 p를 통해 표현된다.
  • 1.25e2 = 1.25 x 10^2 = 125, 1.25e-2 = 1.25 x 10^(-2) = 0.0125
  • 0xFp2 = 15 x 2^2 = 60.0, 0xFp-2 = 15 x 2^(-2) = 3.75
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
// 12.1875값을 갖는 부동소수점 리터럴 표현의 예
  • 숫자 리터럴 값을 표현할 때 사람이 읽기 좋도록 추가적인 formatting을 수행할 수 있다.
  • 추가적인 padding(0) 혹은 underscore(_)을 붙여서 가독성을 높힐 수 있다.
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1

Numeric Type Conversion

  • 어느 정수값이 음수를 갖지 않을지라도 상수 혹은 정수에서 Int를 사용해야한다.
  • 모든 상황에서 기본 Int 타입을 사용하면 정수형 상수 혹은 변수가 즉시 interoperable 해지기 때문이다.
  • 다른 integer 타입은 메모리 사용, 성능 최적화 등과 같이 특수한 경우에 사용해야하며 이 경우 문서화 할 것을 권장한다.

 

Integer Conversion

  • 정수형 상수 혹은 변수에 저장될 수 있는 수의 범위는 각 해당 type에 따라 달라질 수 있다.
  • 예를 들어 Int8[-128, 127], UInt8[0, 255] 범위의 수를 저장할 수 있다.
  • 어느 수가 각 자료형이 지원하는 범위를 넘어간다면 코드가 컴파일 될 때 오류가 발생된다.
let cannotBeNegative: UInt8 = -1
// UInt8 can't store negative numbers, and so this will report an error
let tooBig: Int8 = Int8.max + 1
// Int8 can't store a number larger than its maximum value,
// and so this will also report an error
  • 각 숫자 타입이 저장할 수 있는 범위가 다르기 때문에 상황에 따라 알맞은 타입으로 변환해주어야한다. 예시를 살펴보자!
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
  • twoThousand와 one의 type은 서로 다르기 때문에 기존의 value를 갖으면서 새로운 type을 갖는 수를 생성해야 한다.
  • twoThousandAndOne은 UInt16으로 초기화된다. 왜냐하면 두개의 UInt16값의 합으로 구성되어있기 때문이다.
  • SomeType(ofInitialValue)는 초기값을 갖는 특정 타입의 값을 생성하는 기본적인 방법이다.

 

Integer and Floating-Point Conversion

  • 정수와 부동소수점 사이에 type변환은 반드시 명시적으로 이루어져야 한다.
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double
  • pi는 Double 값의 합으로 구성되었기 때문에 type이 Double로 추론된다.

 

  • 정수형을 부동소수점형으로 변환할 때에도 명시적으로 만들어주어야 한다.
let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int
  • 부동소수점을 정수형으로 바꿔주면 소수점 이하 버려지는 형태로 동작한다
  • Double(4.75) = Int(4), Double(-3.9) = Int(-3)

Type Aliases

  • Type aliases는 기존의 타입에 별명을 붙여주는 기능이며 typealias 키워드를 통해 사용할 수 있다.
typealias AudioSample = UInt16
  • AudioSample 이름을 통해 UInt16 type에 접근할 수 있게 되었다.
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0

Booleans

  • Swift에는 Bool라고 부르는 Boolean type이 있다.
  • 오직 true, false 두가지의 boolean 상수값만을 가질 수 있다.
let orangesAreOrange = true
let turnipsAreDelicious = false
  • Boolean 값은 if문과 같이 조건문에서 유용하게 사용할 수 있다.
if turnipsAreDelicious {
    print("Mmm, tasty turnips!")
} else {
    print("Eww, turnips are horriblr.")
 }
 // Prints "Eww, turnips are horrible."
  • Swift는 type-safe 언어이기 때문에 boolean type이 와야할 곳에 다른 type이 오면 컴파일 할 때 오류가 발생된다.
  • 기본 연산자(Basic Operators)를 통해 boolean type을 생성하여 조건문을 동작시킬 수 있다.
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
}

Tuples

  • tuple은 여러가지 값들을 하나의 복합값으로 묶어준다.
  • tuple 내부를 구선하는 자료형은 서로 동일하지 않아도 된다.
let http404Error = (404, "Not Found")
// http404Error is of type (Int, String), and equals (404, "Not Found")
  • 위 코드와 같이 404라는 정수형과 "Not Found"라는 문자열이 함께 사용되어도 상관이 없다.
  • 개발자가 원하는 자료형으로 구성한 tuple을 생성할 수 있다. (Int, Int, Int) , (String, Bool) 등...
  • tuple을 구성하는 자료를 별개의 상수 혹은 변수로 분해하기 위해 다음과 같은 방법을 사용할 수 있다.
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"
  • 일부의 값을 뽑아오려면 사용하지 않을 부분에 underscore(_)을 사용하면 된다.
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// Prints "The status code is 404"
  • tuple의 개별값에 접근하기 위해 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"
  • tuple의 개별값에 이름을 지정해주어 접근하는 것도 가능하다!
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"
  • tuple은 함수의 return 값으로서 유용하게 사용할 수 있다.
  • 예를 들어 웹페이지 탐색 결과의 성공, 실패 여부를 묘사하기위해 (Int, String) tuple type을 사용할 수 있다.
Note
Tuple은 관련도가 높은 값들을 묶는데 유용하게 사용할 수 있지만 복잡한 자료구조를 생성하는데 적합하지 않습니다. 좀 더 복잡한 자료를 선언해야 한다면 Structures and Classes 문서를 참조하세요!

Optional

  • optional은 값이 존재하지 않을 수 있는 상황에서 사용할 수 있다. 따라서 optional은 두가지 가능성을 표현한다.
  • 값이 존재해서 optional을 벗겨내어 실제 값에 접근할 수 있거나 아무런 값이 존재하지 않거나.
  • optional이 존재하지 않는 값을 다루는데 어떤 방식으로 사용될 수 있는지 살펴보자!
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?", or "optional Int"
  • Swift의 Int type은 String 값을 Int 값으로 변환시켜주는 생성자를 갖고 있다.
  • 모든 String을 Int로 변환할 수 있는 것은 아니다. "123"은 Int로 변환될 수 있지만 "hello, world"는 Int로 변환할 수 없다.
  • 위처럼 실패할 가능성이 있을때 Int 대신에 Int?(optional Int)를 사용하며 생성자는 optional을 사용하여 값을 return 한다.
  • 왜냐하면 성공하면 Int값을, 실패하면 nil값을 넣어주어 오류가 발생하는 것을 예방할 수 있기 때문이다.
  • 자료형 뒤에 ?를 붙여서 optional type으로 선언할 수 있다.
  • Int? 를 선언하면 Int 혹은 nil만 들어갈 수 있으며 string 같은 다른 type은 들어갈 수 없다.

 

nil

  • optional 변수에 대해 nil 값을 할당함으로써 값이 존재하지 않는 상태로 설정할 수 있다.
var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404

serverResponseCode = nil
// serverResponseCode now contains no value
Note
optional이 아닌 상수나 변수에 대해서 nil 값을 사용할 수 없습니다. 특정 상황에서 코드의 상수 혹은 변수에 값이 존재하지 않을 수 있는 경우가 존재한다면 항상 적절한 type을 갖는 optional value로 선언해주세요.

 

  • 만약 기본값을 제공하지 않고 optional 변수를 선언하였다면 변수값이 자동으로 nil값으로 설정됩니다.
var surveyAnswer: String?
// surveyAnswer is automatically set to nil

 

If Statements and Forced Unwrapping

  • optional에 값이 들어있는지 없는지 if문을 통해 확인할 수 있다.
if convertedNumber != nil {
    print("convertedNumber contains some integer value of \(convertedNumber!).")
}
// Prints "convertedNumber contains some integer value."
  • convertedNumber의 값이 nil이 아닐 때 print문을 실행하게 된다.
  • optional 변수 이름 뒤에 !를 붙여서 optional 변수 내부의 값에 접근할 수 있으며 forced unwapping이라고 불린다.
  • nil 상태의 optional에 forced unwrapping을 통해 값에 접근하고자 한다면 runtime error가 발생할 수 있다.
  • forced unwrapping은 optional이 nil값이 아니라는 것이 확실할 때만 사용해야 한다.

 

Optional Binding

  • Optional Binding은 optional을 확인해서 값이 존재하면 해당 값을 임시 상수, 변수에 담아서 사용할 수 있도록 한다.
  • if문, while문에서 optional 값을 검증하고 내부 값을 꺼내오는 용도로 사용할 수 있다.
if let actualNumber = Int(possibleNumber) {
    print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {
    print("The string \"\(possibleNumber)\" couldn't be converted to an integer")
}
// Prints "The string "123" has an integer value of 123"

 

  • 만약 Int(possibleNumber)가 return 하는 optional Int가 값을 갖고 있다면 해당 값을 actualNumber 상수의 값으로 설정한다.
  • 만약 conversion이 정상적으로 수행되었다면 actualNumber가 생성되며 if문의 첫번째 branch 에서 사용할 수 있다.
  • actualNumber은 이미 unwrapping 되어 있기 때문에 값에 접근하기위해 ! 같은 것을 사용하지 않아야 한다.
  • 만약 conversion에 문제가 있어서 nil값이었다면 actualNumber는 생성되지 않으며 if문의 두번째 branch 내용을 실행한다.
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문에 여러개의 optional binding과 Boolean condition을 콤마(,)로 구분하여 포함시킬 수 있다.
  • 중간에 nil이 껴있거나 false 조건이 존재하는 경우 해당 if문 전체가 동작하지 않는다.
Note
if문에서 optional binding을 통해 생성된 변수나 상수는 해당 if문 내부에서 사용할 수 있다. 반면에 guard문을 통해 생성된 상수나 변수는 guard문 이후에 명시된 코드에서 사용할 수 있다. Early Exit을 통해 자세한 내용을 확인해볼 수 있다.

 

Implicitly Unwrapped Optionals

  • optional을 사용하면 if를 사용하여 값이 존재하는지 확인할 수 있고 optional binding을 통해 내부 값에 접근할 수 있도록 unwrapping 할 수 있다.
  • 항상 값을 갖는 것이 보장되는 optional이 있을 수 있다. 이런 경우 매번 옵셔널을 확인하는 절차를 줄일 수 있다면 편해질 것이다.
  • Type!를 붙여서 optional을 만들어줌으로써 implicitly unwrapped optional을 선언할 수 있다.
  • implicitly unwrapped optional은 optional이 처음 선언된 직후와 이후 매 시점에 값이 존재할 것이라고 추정할 수 있는 경우 유용하게 사용할 수 있다.
  • Swift에서는 class 초기화에 주로 사용된다.
let possibleString: String? = "An optional string."
let forcedString: String = possibleString!
// requires an exclamation point

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString
// no need for an exclamation point
  • 암시적 unwrapped optional은 후방에서는 optional로 동작하지만 표면적으로는 optional이 아닌 것처럼 동작한다.
  • 따라서 optional 값에 매번 접근할 때마다 unwrapped 해줄 필요가 없다.
  • implicitly unwrapped optional은 optional에게 필요한 시점에 force-unwrapped 할 수 있는 권한을 주는 것으로 생각해볼 수 있다.
  • Swift는 optional 값으로 최초 시도해본 후 optional로 사용되지 않는다면 force-unwrapped를 사용하여 내부값을 사용한다.
  • 위 코드에서 implicitString은 옵셔널 타입이 아니기 때문에 암시적 unwrapped로 선언된 assumedString은 값을 할당하기 전에 force-unwrapping을 수행한다.
let optionalString = assumedString
// The type of optionalString is "String?" and assumedString isn't force-unwrapped.
  • 위 코드의 경우 어떠한 type도 명시되지 않았기 때문에 보통의 optional type을 갖도록 선언된다.
if assumedString != nil {
    print(assumedString!)
}
// Prints "An implicitly unwrapped optional string."

if let definiteString = assumedString {
    print(definiteString)
}
// Prints "An implicitly unwrapped optional string."
  • 만약 암시적으로 선언된 optional이 nil상태인데 내부 값에 접근하고자 한다면 runtime-error가 발생된다.
  • 보통의 optional과 같은 방식으로 nil 을 검사할 수 있다.
Note
추후에 변수의 값으로 nil이 설정될 수 있는 가능성이 있는 경우 implicitly unwrapped optional을 사용하지 마세요. 변수의 lifetime동안 nil 값을 확인해야 하는 경우 보통의 optional type을 사용하세요.

Error Handling

  • 프로그램이 실행되는 도중에 발생될 수 있는 오류에 대한 처리를 위해 error handling을 사용해야 한다.
  • error handling은 오류 발생 원인을 파악하고 필요하다면 발생한 에러를 다른 부분의 코드로 전달해준다.
func canThrowAnError() throws {
    // this function may or may not throw an error
}​
  • 위 함수가 선언될 때 throws 키워드를 사용함으로써 Error를 던질 수 있다고 명시할 수 있다.
do {
    try canThrowAnError()
    // no error was thrown
} catch {
    // an error was thrown
}
  • Error를 던질(throws) 수 있는 함수를 호출할 때 try 키워드를 사용할 수 있다.
  • Swift는 catch절에 의해 error가 다루어질 때까지 자동으로 error를 현재 scope 밖으로 전파시킨다.
  • canThrowAnError() 를 실행하는데 에러가 발생하지 않으면 그냥 함수를 실행한 것이다.
  • 만약 에러가 발생했다면 해당 에러에 대응되는 catch 구문에서 오류를 받아 처리한다.
func makeASandwich() throws {
    // ...
}

do {
    try makeASandwich()
    eatASandwich()
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)
}
  • makeASandwich() 를 실행할 때 깨끗한 접시가 없거나 재료가 부족하다는 오류가 발생하면 catch 구문을 통해 처리하고 오류가 발생하지 않는다면 eatASandwich()가 실행된다.
  • SandwichError은 개발자가 정의해둔 에러의 종류이며 Error Handling 단원에서 자세하게 살펴볼 수 있다.

Assertions and Preconditions

  • Assertion and Precondition은 runtime시에 발생할 수 있는 것을 확인하는 검사 과정이다.
  • 추가적인 코드를 실행하기전에 기본적인 요건을 충족하는지 확인해볼 수 있다.
  • 중간 과정에서 필수 요건을 충족하지 않는다면 코드 실행이 중단되며 프로그램이 종료된다.
  • 개발 과정의 실수와 잘못된 가정을 찾는데 도움이 된다.

 

  • Assertion and Preconditions는 복구 가능하거나 예상되는 오류에 사용하지 않는다.
  • Assertion에 실패한 경우 프로그램이 잘못된 상태임을 나타내기 때문이다.
  • 이를 사용하여 앱이 더 예측 가능하게 종료되고 문제를 더 쉽게 디버깅하는데 도움된다.

 

  • Assertions와 Precondition의 차이점은 확인되는 시점에 있다. 
  • Assertion의 경우 Debug 빌드에서만 확인된다.
  • Precondition의 경우 Debug 및 Production Build 모두에서 확인된다.
  • Production Build에서 Assertion 내부의 조건을 확인하지 않는다. 
  • 따라서 프로덕션 성능에 영향을 주지 않고 개발 프로세스 중에 원하는 만큼 Assertion을 사용할 수 있다.

 

Debugging with Assertions

  • Swift Standard Library에서 assert(_:_:file:line:) 함수를 호출하여 Assertion을 작성할 수 있다.
let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 isn't >= 0.
  • assert 함수의 조건을 확인하여 true라면 진행, false라면 프로그램을 종료하며 오류 구문이 포함된 경우 함께 출력된다.
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.")
}
  • 조건이 이미 확인된 상태에서 실패를 표현해야 하는 경우 assertionFailure(_:file:line:) 함수를 사용하면 된다.

 

Enforcing Preconditions

  • precondition은 조건이 false일 가능성이 있지만 코드가 실행되기 위해 반드시 True 이어야만 하는 경우에 사용된다.
// In the implementation of a subscript...
precondition(index > 0, "Index must be greater than zero.")
  • preconditionFailure(_:file:line:) 함수를 호출하여 실패가 발생했음을 표현할 수 있다.
반응형