Swift - 옵셔널

이한솔·2023년 7월 30일
0

Swift 문법 🍎

목록 보기
8/32

옵셔널 타입

nil을 사용할 수 있는 타입과 사용할 수 없는 타입으로 구분되는데, 사용할 수 있는 타입이 옵셔널 타입이다. 옵셔널 타입만 nil을 반환할 수 있다.
(오류가 발생할 가능성이 조금이라도 있다면 모두 옵셔널 타입으로 정의해야함)

💡 nil: 값이 없음을 의미하는 특수한 값이다.
실제 값으로는 처리할 수 없는, 무엇인가 문제가 발생했을 때 이를 의미하기 위해 사용한다.

let num = Int("123")
// 출력값 : Optional(123)

let num = Int("swift")
// 출력값 : nil

옵셔널 타입 선언

자료형 뒤에 물음표?만 붙이면 된다.
일반 자료형을 선언만 하고 초기화하지 않으면 아무것도 할당되지 않지만, 옵셔널 타입으로 자료형을 선언하면 자동으로 nil로 초기화된다.

// 옵셔널 Int 타입
var optInt: Int?

// 옵셔널 String 타입
var optStr: String?

// 옵셔널 Double 타입
var optDouble: Double?

// 옵셔널 Array 타입
var optArray: [String]?

// 옵셔널 Dictionary 타입
var optDic: Dictionary<String, String>?
var optDic2: [String: String]?

// 옵셔널 Class 타입
var optClass: AnyObject?

옵셔널 타입 정의

옵셔널 타입의 변수와 상수에 값을 할당하는 방법은 일반 타입과 동일하다.

// 옵셔널 Int 타입
var optInt: Int?
optInt = 3

// 옵셔널 String 타입
var optStr: String?
optStr = "Swift"

// 옵셔널 Array 타입
var optArr: [String]?
optArr = ["C", "Java", "SmallTalk"]

// 옵셔널 Dictionary 타입
var optDic: Dictionary<String, String>?
optDic = ["국어": 94, "수학": 88, "영어": 96]

옵셔널 값 처리

옵셔널 타입은 연산을 지원하지 않는다.
연산을 하려면 옵셔널 객체의 내부의 값을 추출하는 옵셔널 언래핑을 해야한다.

Int("123") + Int("123")
Int("123") + 30
// 연산 불가


명시적 해제

1. 강제적 해제

옵셔널 타입의 값 뒤에 !만 붙여주면 된다. (강제 해제 연산자 사용)
nil일 때 !을 사용하면 오류가 발생하기때문에 nil이 아닐 때만 써야한다.

var optInt: Int? = 3

print("옵셔널 자체의 값 : \(optInt)") // 옵셔널 자체의 값 : Optional(3)
print("!로 강제 해제한 값 : \(optInt!)") // !로 강제해제한 값 : 3

Int("123")! + Int("123")! // 실행결과 : 246
Int("123")! + 30 // 실행결과 : 153


var str = "123"
var intFromStr = Int(str)

if intFromStr != nil {
    print("변환된 값은 \(intFromStr!)입니다")
} else {
    print("값 변환에 실패했습니다")
}
// 출력값 : 변환된 값은 123입니다


var str = "Swift"
var intFromStr = Int(str)

if intFromStr != nil {
    print("변환된 값은 \(intFromStr!)입니다")
} else {
    print("값 변환에 실패했습니다")
}
// 출력값 : 값 변환에 실패했습니다

2. 비강제적 해제 (옵셔널 바인딩)

if let,guard let, if var, guard var
guard 구문은 조건에 맞지 않으면 무조건 함수의 실행을 종료시킨다.
실행 흐름상 옵셔널 값이 해제되지 않으면 더 이상 진행이 불가능할 정도로 큰일이 생길 때 사용하는 것이 좋다.

var str = "Swift"

if let intFromStr = Int(str) {
    print("변환된 값은 \(intFromStr)입니다")
} else {
    print("값 변환에 실패했습니다")
}
// 출력값: 변환된 값은 Swift입니다

func intStr(str: String) {
    guard let intFromStr = Int(str) else {
        print("값 변환에 실패했습니다")
        return
    }
    print("변환된 값은 \(intFromStr)입니다")
}
// 출력값: 값 변환에 실패했습니다.


var capital = ["KR": "Seoul", "EN": "London", "FR": "Paris"]

print(capital["KR"]) // 출력값 : Optional("Seoul")
print(capital["KR"]!) // 출력값 : Seoul
// 강제 해제 연산자 사용
// 딕셔너리의 값은 옵셔널 타입으로 반환함
// 딕셔너리는 존재하지 않는 타입의 키를 사용할 수 있어서 주어진 키에 값이 비어있거나 입력된 키가 아예 없다는 것을 표현하기 위해 nil을 반환함


var capital = ["KR": "Seoul", "EN": "London", "FR": "Paris"]

// 옵셔널 바인딩을 통한 비강제적 해제

if let val = capital["KR"] {
    print(val)
}
// if 구문 안의 로컬 scope에서만 바인딩 된 val 변수 사용 가능

guard let var2 = capital["KR"] else { return }
// var2로 바인딩 성공 시 전역 scope로 val2 변수 사용 가능


묵시적 해제

1. 컴파일러에 의한 자동 해제

비교 연산자를 사용하는 경우 한쪽 타입이 옵셔널, 다른 한쪽이 일반 타입이라면 자동으로 옵셔널을 해제한다.

let optInt = Int("123")

if (optInt == 123) {
    print("optInt == 123")
} else {
    print("optInt != 123")
}
// 실행결과 optInt == 123


let tempInt = Int("123")
tempInt == 123
tempInt == Optional(123)
tempInt! == 123
tempInt! == Optional(123)
// 전부 다 true임

2. 연산자를 사용한 자동 해제

? 연산자 대신 !연산자만 붙여주면 된다.

옵셔널 타입이지만 값을 사용할 때에는 자동으로 옵셔널이 해제되기 때문에 ! 강제 해제 연산자를 사용하여 해제할 필요가 없다. 변수의 값이 nil이 될 가능성이 있다면 묵시적 옵셔널 해제를 사용하지 않아야한다.

💡 변수가 nil이 될 가능성이 있을 때 사용하는 것이 옵셔널 타입인데 nil이 될 가능성이 있으면 사용하지 말라니?
실제로 묵시적 옵셔널이 유용하게 사용되는 경우는 클래스 또는 구조체에서 주로 멤버 변수를 정의할 때 선언과 초기화를 분리시키는 경우이다.

var value1: Int? = 10
value1 + 5 // Error: Value of optional type 'Int?' must be unwrapped to a value of type 'Int'

var value2: Int! = 10
value2 + 5 // 15, 묵시적 해제로 연산도 쉽게 가능


nil coalescing

값이 nil일 경우를 위해 기본값을 설정할 수 있다.

let name: String? = nil
print(name ?? 5) // 5
// name이 nil일 경우 기본값을 5로 설정


Optional chaining

옵셔널은 if 구문을 통해 안전성 여부를 검사하고 사용한다.
클래스나 구조체의 인스턴스가 옵셔널 타입으로 선언될 경우 프로퍼티와 메서드를 호출하기 위해 매번 if 구문을 통해 정상값 여부를 검사해야 한다면 옵셔널 타입을 중첩해서 사용할 때 코드가 계속해서 길어질 것이다.
이러한 옵셔널의 단점을 극복하고 복잡한 코드를 간단하게 줄여주는 것이 옵셔널 체인이다.

struct Human {
    var name: String?
    var man: Bool = true
}

var boy: Human? = Human(name: "홍길동", man: true)


// if 구문 사용시
if boy != nil {
    if boy!.name != nil {
        print("이름은 \(boy!.name!)입니다")
    }
}
// 혹은
if let b = boy {
    if let name = b.name {
        print("이름은 \(name)입니다")
    }
}


// 옵셔널 체인 사용시
boy?.name
// 만약 boy 변수에 nil이 할당되어 있더라도 잘못된 참조에 의한 오류는 발생하지 않는다. 
// 옵셔널 체인의 마지막 값 자체는 옵셔널 체인에 해당하지 않는다. 
// 옵셔널 체인으로 처리할 수 있는 것은 하위 속성이나 메서드를 호출할 때이다. 

// 값을 참조하는 것이 아니라 할당할 때는 다음과 같이 작성하면 된다.
boy?.name = "Peter"

// 옵셔널 체인으로 참조된 값은 무조건 옵셔널 타입으로 반환된다
print(boy?.man)
// Optional(true)

0개의 댓글