오랜만에 쓰는! 스위쁘뜨 기초 문법!! 너무 오랜만이어서.... 호호.... 아무튼.. 옵셔널 가보작오
옵셔널을 직역하면, "선택적인"이다. 선택적이라는 관점에서 바라보면 옵셔널은 값이 있을 수도 있고, 없을 수도 있다라는 것을 알 수 있다! 예를 들어, 아래와 같은 코드가 있다고 가정해 보자.
var name: String = "안녕"
이 코드의 name이라는 변수에 값이 없다라고 하면, var name: String = ""이라고 생각 할 수 있지만, 틀렸따... "" 은 공백을 나타내는 문자열이다!
var name: String? = nil
문자열 변수에 값이 없다라고 하면 위와 같이 작성해야 한다. 또 예를 들어 보자!
var number: Int = 7
위 코드의 number라는 변수에 값이 없다라고 하면 var number: Int = 0이라고 생각할 수도 있겠지만,
var number: Int? = nil
위와 같이 작성해야한다. 하지만 var name: String? = nil 이렇게 작성한 코드는 에러가 날 것이다. 그렇다면, 변수에 nil을 지정하려면 어떻게 해야 할까? 🤔
주목!!!
변수에 nil을 지정하는 방법은 간단하다! 타입 뒤에 ?을 지정해 주면 된다.
var name: String?
이렇게 정의한 변수가 바로 옵셔널이다! 이 옵셔널 변수에 초기값을 지정해 주지 않으면 nil이 지정된다. 옵셔널 변수를 사용하는 이유는 바로 안전성이다! 다른 프로그래밍 언어들은 값이 없을 경우엔 프로그램이 종료되는 참... 슬픈 상황이 발생한다. 하지만 우리의 스위쁘뜨는!! 값이 있을 수도 있고, 없을 수도 있는 옵셔널을 사용하면, 값이 없는 변수에 접근을 하여도 프로그램이 종료되지 않는다는 말씀! 홍홍...
mport UIKit
var name: String? // 옵셔널 타입을 사용하려면 기본 타입 뒤에 ? 붙여 주면 됨!
var optionalName: String? = "SOHEE" // 옵셔널 타입에도 일반 값 할당 가능.
print(optionalName)
// 옵셔널로 정의한 변수는 옵셔널이 아닌 변수와 다름.
// var requiredName: String = optionalName // (1)
(1)의 코드는 아래와 같은 에러가 난다.
에러가 나는 이유는! requiredName은 옵셔널이 아닌 String 타입의 변수이기 때문에 값을 항상 가져야 하는데, optionalName은 옵셔널 변수이기 때문에 코드를 실행하기 전까지는 값이 있는지 없는지 모르므로 에러가 나는 것이다. 따라서 스위프트 컴파일러는 안전을 위해 옵셔널이 아닌 변수에는 옵셔널 변수를 대입할 수 없도록 만들었다! 역시... 똑똑이 스위쁘뜨
위 코드의 실행 결과는 Optional("SOHEE")와 같이 나올 것이다. 이렇게 포장되어 있는 옵셔널 변수는 다른 일반 타입 변수와 결합과 연산이 불가능하다.... 😿 하지만! 바인딩을 하면 가능하다!! 바인딩... 알아보자!!
우선! 옵셔널 해제 방법에 대해 알아보자. 옵셔널 해제 방법에는 크게 명시적 해제와 묵시적 해제가 있다. 명시적 해제 방법에는 강제 해제와 비강제 해제로 나누어지는데, 여기서 비강제 해제가 바로!!! 옵셔널 바인딩이다! 암튼... 묵시적 해제 방법에는 컴파일러에 의한 자동 해제와 옵셔널의 묵시적 해제가 있다. 예제 코드를 통해 더 알아보도록 하겠다!
명시적 해제 방법 - (1) 옵셔널 강제 해제 방법
👉 옵셔널로 선언한 변수 이름 뒤에 !만 붙여 주면 됨.
var number: Int? = 3
print(number)
print(number!)
이 방법은... 비추! 왜냐하면 매우 위험하다. 값이 nil인 변수를 위와 같이 강제로 옵셔널 해제를 하게 된다면, 에러가 발생하여 프로그램이 강제 종료된다.
그렇다면~!! 이번에는 안전하게 옵셔널 해제하는 방법인 옵셔널 바인딩을 알아보자!
명시적 해제 방법 - (2) 옵셔널 비강제 해제 방법 (옵셔널 바인딩)
var number: Int? = 3
/* if (옵셔널을 추출해서 할당받을 변수나 상수) = (옵셔널 값) {
} */
if let result = number {
print(result) // 만약 값 추출이 실패하면, else 문이 실행됨.
} else {
}
// guard문
func test() {
let number: Int? = 5
guard let result = number else { return }
print(result)
}
test()
옵셔널 바인딩은 if문, while문, guard문에서 사용한다. 기본 문법을 알아보자.
//if 문
if let Name: Type = OptionalExpression {
Statements
}
//while문
while let Name: Type = OptionalExpression {
Statements
}
//guard문
guard let Name: Type = OptionalExpression else {
Statements
// 바인딩에 실패할 경우 else블록이 실행됨.
}
if문을 이용해 옵셔널 바인딩을 하면, 옵셔널이 추출되는 상수나 변수를 if문 블록 내에서만 사용 가능하지만, guard문을 이용하여 옵셔널 바인딩을 한다면 guard문 다음, 함수 전체 구문에서 추출된 변수나 상수를 사용할 수 있다. guard문은 조건이 true일 때만 guard문을 통과하고, false인 경우 else문을 실행한 뒤 흐름을 종료시킨다. guard문은 다음에... 더 알아보도록 하겠다! 이번 파트에서는 guard문을 이용해 옵셔널 바인딩을 할 수 있다는 것만 알아두기로 하자! 👏
묵시적 해제 방법 - (1) 컴파일러에 의한 자동 해제
옵셔널 값을 비교 연산자를 이용해 다른 값과 비교하면, 컴파일러가 옵셔널 값을 자동적으로 해제하는 방법.
let value: Int? = 6
if value == 6 {
print("value가 6입니다.")
} else {
print("value가 6이 아닙니다.")
}
묵시적 해제 방법 = (2) 옵셔널의 묵시적 해제
// 묵시적 해제는 옵셔널 타입이지만, 값을 사용할 때는 자동으로 옵셔널이 해제된다.
let string = "12"
var stringToInt: Int? = Int(string) // String을 Int로 반환하는 값을 넘겨 받음
// print(stringToInt + 1) // error
만약, string 변수에 문자열이 숫자가 아닌 한글 혹은 영어와 같은 다른 문자가 있다면 반환하는 함수가 nil을 반환하기 때문에 반환 타입이 옵셔널 Int이다. 그렇기 때문에 stringToInt는 옵셔널로 선언되어야 하고, ?를 !로 바꾸어주면, 묵시적 옵셔널 해제가 된다.
let string = "12"
var stringToInt: Int! = Int(string)
print(stringToInt + 1)
타입 뒤에 !이 붙어 있는 stringToInt 변수는 사용할 때 옵셔널이 묵시적으로 해제되어 일반 값처럼 자유롭게 연산이 가능하다!