기초
- Type
- Int
- Double
- Float
- Bool
- String
- Colletion Type
- Array
- Set
- Dictionary
- Tuple(swift only, return multiple values)
- 변수
- 변수 변경여부
- optional type
- 값이 있는지 없는지
- 값이 있고 x와 같다 or 값이 없다
- nil과 유사하지만 swift에서 아주 중요하다!
- 모드 타입에서 사용가능하다.
- 특징
- type safe 언어
- String으로 정의하면 거기에 Int를 넣는 경우 compile error가 된다.
상수와 변수
선언
var x = 0.0, y = 0.0, z = 0.0
- 주의 : 코드에서 저장한 값이 변경되지 않는다면 let 키워드를 이용해서 상수로 선언하라. 변경된다면 변수로 선언한다.
타입 어노테이션 (Type Annotations)
- 상수나 변수를 선언할 때 타입 어노테이션을 사용한다. 상수나 변수에 저정할 값의 종류를 명확히 한다. 상수와 변수 이름 뒤에 콜론과 스페이스를 넣고 타입 어노테이션을 쓴다.
- welcomeMessage라는 변수에 String 값으로 타입 어노테이션을 사용하면 아래와 같다.
var welcomeMessage: String
- welcomeMessage라는 String 타입의 변수를 선언한다
- 이 말은 String 값이면 이 변수에 저장할 수 있다는 뜻이다.
- 변수이므로 향후에 다른 String 값을 저장할 수도 있다.
welcomeMessage = "Hello"
- 동일한 타입의 여러 변수를 한 줄에 표현이 가능하다.
var red, green, blue: Double
- 주의: 사실 타입 어노테이션을 사용할 일은 드물다. 정의하는 시점에 상수나 변수에 초기값을 제공하는 경우 항상 변수의 타입을 추측할 수 있다. 위 예제에서 welcomeMessage 변수는 초기 값으로 타입을 추측할 수 있다.
이름 붙이기
- 상수와 변수 이름은 유니코드 문자를 포함해서 어떤 문자든 가능하다.
let 파이 = 3.14159
let 환영 = "안녕세계"
- 상수나 변수 이름에는 공백, 수학기호, 화살표, 라인이나 박스그리기 문자는 사용할 수 없다.
- 숫자는 시작하는 경우는 안된다. 하지만 중간에 숫자가 포함되는 것은 가능하다.
- 일단 한 번 이름을 선언하면 동일 이름으로 선언하는 것은 허용하지 않는다.
- 상수를 변수로 변경할 수 없고 변수를 상수로 변경할 수도 없다.
- 주의 : 예약된 Swift 키워드로 상수와 변수에 동일한 이름을 줘야한다면, 이름으로 사용한다면 (`) 키워드로 감싸야한다.
- 기존 변수의 값을 호환가능한 타입의 다른 값으로 변경할 수 있다. 이 예제에서 friendlyWelcome의 값은 "Hello~"에서 "Bonjour!"로 변경한다
var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
- 변수와 달리 일단 설정되면 상수 값은 변경할 수 없다. 변경을 시도하면 컴파일 시에 에러를 반환한다.
let languageName = "Swift"
languageName = "Swift++"
출력
- print 함수로 현재 변수의 값을 출력한다.
print(friendlyWelcome)
- print(_:separator:terminator:)함수는 전역 함수로 하나 이상의 값을 출력할 수 있다. Xcode에서 print 함수는 Xcode의 콘솔에 출력한다.
- separator와 terminator 인자는 기본 값을 가지고 있어서 호출시에 생략할 수 있다. 출력할 때 끝에 라인 브레이크를 추가해서 출력한다. 라인 브레이크 없이 값을 출력하면 terminator로 빈 문자를 전달한다. 예제로 print(someValue, terminator: "")와 같다.
- Swift는 긴 문자에서 상수와 변수 이름을 포함하기 위해서 문자열 보간법(string interpolation)을 사용한다. 괄호내에 이름을 감싸고 괄호 시작 전에 백슬래시 사용한다.
print("The current value of friendlyWelcome is \(friendlyWelcome)")
- string interpolation과 함께 사용할 수 있는 옵션은 string interpolation에 참고하자.
코멘트
- 코드에 실행되지 않는 구문을 넣을 때 코멘트를 사용한다. 주로 개발자를 위한 노트나 기억해야할 내용을 기입힌다. Swift에서 컴파일 될 때 코멘트 부분은 처리하지 않는다.
- 다른 언어의 코멘트와 기능이 돌일하다.
- 여러 줄로 코멘트는 작성하는 경우 (/)로 시작하고 (/)로 끝낸다.
- C언어에서 코멘트와 차이점은 다른 여러 줄 코멘트 내에 코멘트를 추가할 수 있다는 것이다.
세미콜론
정수 범위
Int
- 대부분 경우, 코드에서 정수의 특정 사이즈를 고르지 않아도 된다. Swift는 추가적으로 정수 타입인 Int를 제공한다. 이는 사용하는 플랫폼의 기본 word size와 같다.
- 32bit 플랫폼에서 Int는 Int32와 같다.
- 64bit 플랫폼에서 Int는 Int64와 같다.
- 정수의 특정 사이즈가 필요하지 않는 경우에는 그냥 Int 타입을 사용하자. 이렇게 사용하면 코드의 일관성과 interoperability를 돕는다. 32bit 플랫폼에서 Int는 -2,147,483,648과 2,147,483,647 사이의 값을 사용하며 이는 일반 사용에서 충분한 범위이다.
UInt
- unsigned int 타입으로 UInt를 제공한다. 해당 플랫폼의 word 크기와 동일한 크기를 가진다.
- 32bit 플랫폼에서 UInt32와 동일
- 64bit 플랫폼에서 UInt64와 동일
- 주의: 플랫폼의 word 크기와 동일한 크기의 unsigned integer 타입을 필요할 때만 UInt를 사용하라. 이런 경우가 아니라면 Int를 사용하는 것을 추천한다. 음수값을 저장해야할 경우라도 마찬가지다. Int를 사용하면 interoperability를 높여서 다른 숫를 나타내는 타입 사이에서 변환할 필요를 없애준다. 그리고 정수 타입 추측을 사용시 매치가 되는 장점도 있다.
소수점 숫자(Floating-Point Numbers)
- 소수점을 나타내는 경우. (3.14, 0.1 등)
- 정수 타입 값보다 더 넓은 범위의 값을 표현할 수 있다. Swift는 2가지 소수점 숫자 타입을 제공한다.
- Double : 64bit 소수점 숫자 표현
- Float : 32bit 소수점 숫자 표현
- 주의 : Double은 적어도 15자리 숫자를 표현할수 있다. Float은 6자리 숫자를 표현할 수 있다. 여러분 코드에서 필요한 값의 범위에 따라 적절한 소수점 타입을 골라서 사용한다. 추천하는 타입은 Double이다.
타입 안전성과 타입 추측
- Swift는 타입 안정성을 가지는 언어다. 코드에서 값의 타입에 대해서 명확히 해야한다. String을 받기로 하는 부분에서 Int를 넘겨주면 컴파일 에러가 발생한다.
- 타입 안정성에서는 타입 검사를 수행한다. 컴파일 하는 시점에 타입이 일치하지 않는 경우 에러로 표시한다. 이렇게 하면 개발시에 가능한한 빨리 에러를 알아채고 수정할 수 있게 한다.
- 타입 검사는 값에 다른 타입을 사용할 때 에러를 회피할 수 있게 돕는 역할을 한다. 하지만 모든 상수와 변수에 타입을 지정해야한다는 것은 아니다. 여러분이 필요한 값의 타입을 지정하지 않더라도 Swift는 적절한 타입을 추측한다.(type inference) 타입 추측은 컴파일러가 컴파일을 하는 시점에 자동으로 적절한 타입을 추측하는 것이다. 변수에 대입할 값을 검사함으로써 알아낸다.
- 타입 추측 때문에, Swift에서는 다른 언어에 비해서 타입 선언하는 부분이 훨씬 적다. 상수와 변수는 여전히 명시적으로 타입을 명시할 수 있지만 이렇게 하면 여러분이 직접 타입을 지정하는 일을 해야만 한다.
- 타입 추측은 초기 값으로 상수와 변수를 선언할 때 특히 유용하다. 변수 선언할 때, 리터럴 값을 상수와 변수에 할당하는 방식으로 이를 수행할 수 있다. 리터럴 값은 소스코드에서 직접 값을 넣는 방식을 말한다.)
- 리터럴 값인 42를 타입 없는 새로운 상수에 할당하는 경우 Swift는 Int 타입인 상수라고 추측한다.
let meaningOfLife = 42
- 소수점 리터럴을 위해 타입을 지정하지 않는다면 Swift는 Double 타입의 값을 생성하길 원한다고 추측한다.
let pi = 3.14159
- Swift는 소수점 숫자의 타입을 추측하는 경우에 항상 Double을 선택한다.(Float이 아니다.)
- 정수형과 소수점 리터럴이 함께 사용하는 경우 Double 타입으로 추측한다.
let anotherPi = 3 + 0.14159
- 3의 리터럴 값은 자체로 명확한 타입을 가지지 않고 적절한 Double 타입 결과가 소수점 리터럴의 있다는 것에서 추측할 수 있다.
숫자 리터럴
- 정수 리터럴은 아래와 같이 작성한다
- 10진수 : 아무것도 붙이지 않는다.
- 2진수 : 0b를 앞에 붙인다.
- 8진수 : 0o를 앞에 붙인다.
- 16진수 : 0x를 앞에 붙인다.
- 10진수 17일 표현하는 예제는 아래와 같다.
let decimalInteger = 17
let binaryInteger = 0b10001
let octalInteger = 0o21
let hexadecimalInteger = 0x11
- 소수점 리터럴은 10진수나 16진수로 표현이 가능하다. 항상 10진수 포인트의 양면을. 10진 소수점 수는 또한 exponent를 가질 수 있다. 대소문자로 e로 표시한다. 16진수 소수점은 대수문자 p로 표시한다.
- exp로 10진수에 대해서 base number는 10exp을 곱한다.
- 1.25e2 : 1.25 x 10 2 혹은 125.0
- 1.25e-2 : 1.25 x 10 -2 혹은 0.0125
- exp로 16진수를 나타내면 base number는 2exp로 곱한다.
- 0xFp2 : 15 x 2 2 혹은 60.0
- 0xFp-2 : 15 x 2 -2 혹은 3.75
- 소수점 리터럴 모두 10진수로 12.1875 값이다.
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
- 숫자 리터럴은 읽기 쉽게 하기 위해서 추가로 포맷을 포함할 수 있다. 정수와 소수는 추가로 0을 삽입하고 _를 포함할 수 있다. 포맷은 리터럴 기본 값에 어떤 영향도 미치지 않는다.
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
숫자에서 타입 변환
- 일반적인 목적으로 정수 상수와 변수에 대해서 Int 타입을 사용하라. 기본 정수 타입을 사용은 정수 상수와 변수는 즉시 interoprable해서 정수 리터럴 값에 대해서 타입 추론으로 사용할 수 있다.
- 특정 목적으로 사용하는 경우 다른 정수 타입을 사용하라. 외부 소스에서 명시적으로 크기를 가지는 값, 성능, 메모리 사용, 필요한 최적화 등의 필요가 있을 경우를 말한다. 이런 경우 명시적으로 특정 크기 타입을 사용하면 사고로 값이 오버플로가 발생하는 경우를 잡아낼 수 있고 이런 것을 암시적으로 속성을 나타내는 역할을 한다.
정수 변환
- 정수를 나타내는 상수나 변수에 저장할 수 있는 범위는 숫자 타입에 따라 다를 수 있다. Int8 상수나 변수는 -128과 127 사이의 값을 저장할 수 있다. UInt8의 경우 0에서 255 사이 값이 가능하다. 이 범위에 맞지 않는 값을 넣는 경우 컴파일 하는 경우 에러가 발생한다.
let cannotBeNegative: UInt8 = -1
let tooBig: Int8 = Int8.max + 1
- 각 숫자 타입은 다른 범위의 값을 가지므로 숫자 타입 변환하는 경우 신경을 써야한다. 타입 변환에서 의도를 명시적으로 나타내므로 숨겨진 변환 에러를 방지할 수 있다.
- 특정 숫자 타입을 다른 타입으로 변환하는 경우, 기존 값과 원하는 타입의 새로운 수로 초기화한다. 상수 twoThousand는 UInt16 타입이고 반면에 상수 one은 UInt8이다. 동일한 타입이 아니므로 함께 직접 더하기를 할 수 없다. 대신에 이 예제는 UInt16(one)을 호출해서 새로운 UInt16 타입으로 one 값을 초기화하고 원래 값을 대신에서 사용한다.
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
- 더하기의 경우 양쪽모두 UInt16이므로 문제가 없다. UInt16 2개 값을 더하므로 결과 상수는 UInt16으로 추측한다.
- SomeType(ofInitialValue)은 Swift 타입의 초기화를 호출하고 초기화 값으로 전달하는 일반적인 방식이다. 실제로는 UInt16은 UInt8 값을 받아들이는 초기화를 가지며 이 초기화는 기존 UInt8에서 새로운 UInt16을 만들어서 초기화로 사용한다. 기존 타입을 확장해서 새로운 받아들여 초기화를 제공하는 것은 Extension을 참고하자.
정수와 소수 변환
- 정수와 소수 타입 사이에 변환은 만드시 명시적이여야 한다.
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
- 여기서 상수 three의 값은 Double 타입의 새로운 값을 생성한다. 더하기의 양쪽이 동일한 타입이 된다. 이런 변환이 없으면 더하기 시에 에러가 발생한다.
- 소수를 정수로 변환하는 경우 반드시 명시적이여야 한다. 정수 타입은 Double이나 Float 값으로 초기화할 수 있다.
let integerPi = Int(pi)
- 이런 방식으로 부동 소수점 값을 새로운 정수 값으로 초기화하는 경우 길이가 줄어든다. 이 말은 4.75는 4가 되고 -3.9는 -3이 된다.
- 주의: 수를 나타내는 상수와 변수를 결합하는 경우의 법칙과 숫자 리터럴의 경우에 적용되는 원칙과 차이가 있다. 리터럴 값 3은 바로 리터럴 값 0.14159와 더하기를 할 수 있다. 숫자 리터럴은 명시적인 타입을 가지지 않으므로 가능하다. 타입은 컴파일러가 처기하는 시점에만 추측할 수 있다.
타입 앨리어스
- 기존 타입에 대해서 다른 이름을 정의할 수 있다. 타입 앨리어스를 정의할 때 typealias 키워드를 사용한다.
- 기존 타입을 참조할 때 콘텍스트에서 좀더 적합한 이름으로 기존 타입을 사용할 수 있다. 외부 소스에서 특정 사이즈의 데이터로 동작하는 경우가 좋은 예제다.
typealias AudioSample = UInt16
- 일단 타입 앨리어스를 정의하면 원래 이름을 사용할 수 있는 모든 곳에 사용할 수 있다.
var maxAplitudeFound = AudioSample.min
- AudioSample은 UInt16 타입에 대한 별칭으로 정의할 수 있다. 별칭이므로 AudioSample.min은 실제로 UInt16.min을 호출하는 것과 같다. maxAmplitudeFound 변수에 대해서 0 초기화 값이 제공된다.
불린 (Booleans)
- 불린 타입을 가지며 Bool이라고 쓴다. 불린 값은 참/거짓을 가지며 논리값으로 사용된다. Swift에서는 true/false와 같은 불린 상수 값을 제공한다.
let orangesAreOrange = true
let turnipsAreDelicious = false
- orangesAreOrange와 turnipsAreDelicious 타입은 Bool 타입으로 추측한다. 왜냐하면 Bool 갑승로 초기화되었기 때문이다. 앞에서 봤던 Int와 Double과 같이 Bool이라는 타입을 쓰지 않아도 추측할 수 있다. 생성할때 true나 false로 설정하면 생성할 때 Bool 타입으로 생성한다. 타입 추측은 코드를 간결하고 일기 쉽게 만들어 준다.
- 불린 값은 특별히 if 문과 같이 조건문에 적합하다.
if turnipsAreDelicious {
print("Mmm, tasty turnips!")
} else {
print("Eww, turnips are horrible.")
}
- if와 같은 조건문은 제어문에서 상세히 다룬다.
- Bool을 대체해서 다른 타입이 사용되는 경우 컴파일 에러를 발생시킨다.
ift
let i = 1
if i {
//컴파일 에러
}
- 그러나 아래와 같은 다른 예제에서는 유효하다.
let i = 1
if i == 1 {
}
- i==1 비교 결과는 Bool 타입이 되므로 두번째 예제는 성공적으로 컴파일 된다.
- 타입 안정성의 다른 예제로 이런 접근법은 우연히 발생하는 에러를 회피하고 코드의 특별한 의도를 명확히한다.
튜플(Tuples)
- 여러 값을 하나의 복합체 값으로 묶어주는 역할을한다. 튜플 내부에 값은 어떤 타입도 가능하고 서로 동일한 타입을 필요도 없다.
- 이 예제로 (404, "Not Found")는 HTTP status code를 설명하는 튜플이다. 웹페이지를 요청할 때마다, HTTP 상태 코드는 웹서버가 반환하는 특별한 값이다. 404 Not Found 상태 코드는 웹페이지가 존재하지 않는다는 것을 뜻한다.
let http404Error = (404, "Not Found")
- (404, "Not Found") 튜플은 HTTP 상태 코드를 나타내는데 Int와 String을 함께 묶어서 표현한다. 2개 값은 각각 숫자와 사람이 읽을 수 있는 설명이다. 이를 (Int, String) 타입의 튜플이라고 한다.
- 타입을 순서로 나열하는 방식으로 튜플을 생성하며 다양한 다른 타입을 포함할 수 있다. (Int, Int, Int)나 (String, Bool)과 같은 타입의 튜플을 가질 수 있으며 원하는 순서로 나열할 수 있다.
- 튜플의 내용을 분리 상수나 변수로 분해하여 접근이 가능하다.
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
print("The status message is \(statusMessage)")
- 튜플의 일부 값만 필요한 경우 _를 사용해서 필요없는 부분을 무시할 수 있다.
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
- 대안으로 개별 값에 접근하는 경우 0으로 시작하는 인덱스 값으로도 가능하다.
print("The status code is \(http404Error.0)")
print("The status message is \(http404Error.1)")
- 튜플을 정의할 때 튜플내에 개별 엘리먼트에 이름을 붙일 수 있다.
let http200Status = (statusCode: 200, description: "OK")
- 튜플의 엘리먼트에 이름을 붙이면 그 이름으로 값에 접근할 수 있다.
print("The status code is \(http200Status.statusCode)")
print("The status message is \(http200Status.description)")
- 튜플은 함수의 반환 값으로 사용하는 경우 특히 유용하다. 웹베이지를 불러오는 함수는 (Int, String) 튜플 타입을 반환해서 성공이나 실패 여부를 설명할 수 있다. 2가지 값을 가지는 튜플을 반환함으로 단일 타입의 값을 반환하는 경우보다 결과에 대해 보다 유용한 정보를 제공할 수 있다.
- 주의: 튜플은 관련 값의 임시 그룹에 유용하다. 복잡한 자료구조를 생성하기에 적합하지 않다. 여러분의 자료구조가 임시 범위를 벗어나는 경우, 튜플보다는 클래스나 구조체로 모델링이 된다.
옵셔널
- 값이 없을 수도 있는 경우 옵셔널을 사용한다. 옵셔널의 의미는 :
- 값이 있고 이것은 x와 같다
- 값이 존재하지 않는다.
- 주의 : Swift에서는 특별한 상수가 필요없이 어떤 타입의 값이 존재하지 않을 수 있다는 것을 표현한다.
- 값이 없는 경우 옵셔널이 어떻게 사용하는데 예제가 있다. Swift의 Int 타입은 초기화를 가지는데 String을 Int 값으로 변환을 시도한다. 그러나 모든 문자열이 정수로 변환할 되지는 않는다. 문자열 "123"은 숫자 123으로 변환될 수 있다. 하지만 문자열 "hello, world"은 변환할 명확한 숫자 값을 가지지 않는다.
- 아래 예제는 String을 Int로 변환을 시도하는 초기화를 사용한다.
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
- 초기화가 실패할 수도 있으므로, Int가 아니라 옵셔널 Int를 반환한다. 옵셔널 Int는 Int?로 쓴다. ? 마크는 포함하는 값이 옵셔널하다는 것을 표시한다. Int 값을 포함하거나 값이 없을 수 있다는 것을 뜻한다. (Bool 값이나 String 값을 포함할 수는 없다. Int 이거나 없어야 한다.)
nil
- nil이라는 특별한 값을 할당하면 값이 없는 상태로 옵셔널 변수를 설정한다.
var serverResponseCode: Int? = 404
serverResponseCode = nil
- 주의: nil은 nonoptional 상수와 변수로 사용할 수 없다. 상수와 변수가 값이 없는 경우 항상 해당 타입의 옵셔널 값으로 선언해야한다.
- 기본 값을 제공하지 않고 옵셔널 변수를 정의하면 변수는 자동으로 nil로 설정된다.
var surveyAnswer: String?
- 주의: Swift의 nil과 다른 언어의 nil은 차이가 있다. nil은 포인터가 아니라 특정 타입의 값이 존재하지 않는다는 것을 뜻한다. 어떤 타입의 옵셔널도 nil로 설정할 수 있으며 이는 객체 타입에 한정되는 것이 아니다.
If 구문과 강제 unwrapping
- 옵셔널이 값을 포함하고 있는지 여부를 if 구문을 통해 확인할 수 있다. 이때 nil인지 비교를 통해 확인한다. (== 이나 !=)
- 옵셔널이 값을 가지고 있으면 nil과 같지 않다고 한다.
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
옵셔널 바인딩(Optional Binding)
- 옵셔널이 값을 포함하는지 여부를 알아보기 위해서 옵셔널 바인딩을 사용한다. 포함하고 있다면 임시 상수나 변수로 값을 사용할 수 있다. 옵셔널 값의 내부를 검사하기 위해서 옵셔널 바인딩은 if나 while 구문과 함께 사용할 수 있고 상수나 변수로 값을 한 번에 추출해 낼 수 있다.
- if 구문으로 옵셔널 바인딩 작성법
if let constantName = someOptional {
statements
}
- forced unwrapping보다 옵셔널 바인딩을 사용하기 위해서 옵셔널로부터 possibleNumber 예제를 다시작성할 수 있다.
if let actualNumber = Int(possibleNumber) {
print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
} else {
print("\'\(possibleNumber)\' could not be converted to an integer")
}
- 위 코드는 아래와 같이 읽을 수 있다.
- Int(possibleNumber)에서 반환되는 옵셔널 Int는 값을 포함하고 있다면, 새로운 actualNumber 상수를 옵셔널에 포함된 값으로 설정한다.
- 변환이 성공하면 actualNumber 상수는 if 구문의 처음 브랜치내에서 사용 가능하게 된다. 옵셔널 내부에 포함된 값으로 이미 초기화되면 값에 접근하기 위해서 !를 사용할 필요가 없게 된다. 여기 예제에서 actualNumber는 단순히 변환의 결과를 출력하는데 사용된다.
- 옵셔널 바인딩과 함께 상수와 변수 모두 사용할 수 있다. actualNumber의 값을 처리하기 원하면 if var actualNumber로 작성할수 있고, 대신 옵셔널 내부에 포함된 값은 상수보다는 변수로 유효하게 된다.
- if 구문에서 여러 옵셔널 바인딩을 포함할 수 있고 where 절을 사용해서 불린 조건을 검사할 수 있다.
if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber < secondNumber {
print("\(firstNumber) < \(secondNumber)")
}
암시적 Unwrapped Optionals
- 위에서 본 것과 같이 옵셔널은 상수와 변수가 값이 없다는 것을 허용한다. 옵셔널은 if 구문으로 값 존재여부를 확인하고 존재하는 경우 옵셔널의 값에 접근하기 위해 옵셔널 바인딩으로 조건적으로 unwrap이 가능하다.
- 가끔 항상 값을 가지는 프로그램의 구조체로부터 명확해지면 해당 값이 먼저 설정된 후이다. 이런 경우에 매번 옵셔널 값의 검사하고 unwrap할 필요성을 없애준다. 왜냐하면 항상 값을 가진다는 것을 가정하기 때문이다.
- 옵셔널의 이런 종류는 암시적 unwrapped optional이라고 정의한다. 옵셔널로 만들기를 원하는 타입 뒤에 ? 마크 대신에 ! 마크를 사용해서 암시적 unwrapped optional을 사용한다.
- 옵셔널이 먼저 정의되고 모든 포인터에서 값이 존재한다고 가정한 후에, 암시적 unwrapped optional은 옵셔널 값이 이미 존재한다는 것을 확신할 때 유용하다. 암시적 unwrapped optionas의 주요 사용은 클래스 초기화할 떄이다.
- 암시적 unwrapped optional은 일반적인 옵셔널이지만 nonoptional 값처럼 사용할 수 있다. 매번 접근할 때마다 옵셔널 값을 unwrap할 필요가 없다. 다음 예제에서는 wrapped 값에 명시적인 String으로 접근하는 경우에,옵셔널 문자열과 묵시적 unwrapped optional 사이의 차이나는 동작을 보여준다.
let possibleString: String? = "An optional string."
let forcedString: String = possibleString!
let assumedString: String! = "An implicitly unwrapped ooptional string."
let implicitString: String = assumedString
- 암시적 unwrapped optional를 옵셔널에 대해서 사용할때마다 자동으로 unwrap할 수 있도록 권한을 주는 것으로 생각할 수 있다. 사용하는 매번 옵셔널 이름 뒤에 ! 마크를 넣는 것대신 선언할 때, 옵셔널 타입 뒤에 ! 마크를 넣는다.
- 주의 : 암시적 unwrapped optional이 nil이고 wrapped 값에 접근하려고 하면 런타임 에러가 발생한다. 이 결과는 값을 포함하지 않는 일반 옵셔널 뒤에 ! 두는 것과 정확히 같다.
- 값을 포함하는지 여부를 검사하기 위해서 암시적 unwrapped optional을 일반 옵셔널로 취급할 수도 있다.
if assumedString != nil {
rpint(assumedString)
}
- 단일 구문에서 값을 검사하고 unwrap하기 위해서 옵셔널 바인딩과 함께 암시적 unwrapped optional을 사용할 수 있다.
if let definiteString = assumedString {
print(definiteString)
}
- 주의: 변수가 nil이 될 가능성이 있다면 암시적 unwrapped optional을 사용하지마라. 변수를 사용하는 동안 nil 값을 검사할 필요가 있다면 항상 옵셔널 타입을 사용하라.
에러 처리(Error Handling)
func makeASandwich() throws {
}
do {
try makeASandwich()
eatASandwich()
} catch Error.OutOfCleanDishes {
washDishes()
} catch Error.MissingIngredients(let ingredients) {
buyGroceries(ingredients)
}
- makeASandwich() 함수는 깨끗한 접시가 없거나 재료가 없는 경우 에러를 던진다. makeASandwich()가 던지므로 함수 호출은 try 부문에서 사용한다. do 구문내에 함수 호출이 들어가 있으므로 발생하는 모든 에러는 제공하는 catch 절로 전파된다.
- 어떤 에러도 발생하지 않으면, eatASandwich() 함수가 호출된다. 에러가 발생하고 Error.OutOfCleanDishes랑 매치되고 washDishes() 함수가 호출될 것이다. 만약 발생한 에러가 Error.MissingIngredients 경우라면 buyGroceries 함수는 catch 패턴으로 캡쳐한 관련된 [String] 값과 함께 호출된다.
- 자세한 내용은 에러처리에서 다루기로 한다.
Assertions
- 특정 조건을 만족시키지 못하는 경우에 코드를 계속 실행하기 불가능할 수 있다. 이런 경우에 코드에서 assertion을 발생시켜서 실행을 중단하고 디버깅할 수 있는 기회를 제공할 수 있다.
Assertion으로 디버깅
- assertion은 불린 조건이 확실히 true인지를 실행시에 검사한다. 문자그대로 조건이 참인지를 assert한다. 다음 코드를 실행하기 전에 원래 조건을 만족시키는지를 확신하기 위해서 assertion을 사용한다. 조건이 참이면 코드는 일반적인 경우와 같이 코드 실행을 계속한다. 만약 조건이 거짓이면, 코드 실행을 중단하고 app을 중단시킨다.
- 여러분의 코드가 디벅이 환경에서 실행되는 동안 assertion을 발생시키면, assertion이 발생한 시점에 문제가 발생한 곳과 상태를 확인할 수 있다. assertion의 속성 중에 적절한 디버깅 메시지를 제공하는 것도 있다.
- Swift 표준 라이브러리의 전역 함수인 assert 함수를 호출하면 assertion을 사용할 수 있다. 이 함수에 참/거짓을 평가할 수 있는 표현과 거짓인 경우 표시할 메시지를 인자로 전달한다.
let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
- 이 예제에서 코드 실행은 age >= 0 이 참인 경우에만 계속 진행된다. 즉 age 값이 음수가 아니여야 한다. 만약 age가 음수면 age >= 0 은 거짓이 되므로 assertion이 발생하고 app이 종료된다.
- assertion 메시지는 원하면 생략할 수 있다. 다음과 같다.
assert(age >= 0)
- 주의: assertion은 컴파일 최적화를 하는 경우 동작하지 않는다. Xcode에서 릴리즈 모드로 설정해서 필드하는 경우이다.
Assertion 사용하는 경우
- 조건이 잠재적으로 false가 될 수 있을 때마다 assertion을 사용하라. 하지만 코드가 계속 실행되기 위해서는 반드시 참이여야 하는 경우여야 한다. assertion 검사에 대한 적절한 시나리오는 다음과 같다.
- 정수 subscript index가 범위를 벗어나는 경우
- 값이 함수에 전달되지만, 유효하지 않은 값은 해당 함수가 자신의 타스크를 수행할 수 없다는 것을 의미할 때
- 옵셔널 값이 현재 nil이지만 이후 코드가 성공적으로 실행되기 위해서 nil이 아닌 값이 와야하는 경우
- 주의: assertion은 app을 종료시키며 유효하지 않은 상황이 발생하지 않을 것이라는 방식에서 코드를 설계하는 것이 아니다. 그럼에도 불구하고 이런 상황에서 유효하지 않은 조건은 가능하고 assertion은 app이 공개되기 전에 개발하는 단계에서 이런 조건을 알아채기 위한 효과적인 방법이다.