iOS/Swift 상식

Swift - Optional

HJ39 2023. 1. 23. 21:44

Optional은 Swift를 처음 접해본 사람들이 헷갈리는 부분들 중 한 부분을 담당하고 있다.

 

간단하게 설명하자면 Optional은 변수에 값이 있을 수도 있고 없을 수도 있다.

즉 nil(값이 없음)을 허용할 것인지 허용하지 않을 것인지 결정하는 기능이다.

 

nil은 다른 언어의 NULL과 다르다.

nil은 해당 공간에 값이 없는 경우 값 대신 nil을 사용하지만 

다른 언어의 NULL은 빈 포인터를 가리킬 때 NULL을 사용한다.

 

Optional을 쉽게 이해하기 위해 예를 들어 생각해 보자

(예시를 설명하는 문장은 기울임 꼴을 적용하였다.)

 

어떤 변수에 Optional을 붙인다는 것

우리가 호텔을 체크인하는 것과 같다.

체크인을 하면 방을 배정받게 된다.

 

 

Optional을 사용하면 Wrapping이 되어 있는 상태이다.

Wrapping이라는 것은 무엇을 포장지로 덮는다는 의미이다.

예시에 적용해 보자

호텔을 체크인하여 방에 들어간 상황을 떠올리면 된다.

Optional을 출력하는 경우 호텔 측 입장에서 방에 손님이 있는지 없는지 알 수 없다.

 

그러므로 Optional을 출력하는 경우 Optional(변수에 저장된 값)으로 출력된다.

 

Unwrapping 하기 위해서는 다음과 같은 방법이 존재한다.

  • ! (Forced Unwrapping)
  • ??
  • Optional Binding 
    • guard-let
    • if-let 
  • Optional Chaining

 

 

! 사용 (Forced Unwrapping)

! 를 사용하는 경우 강제적으로 옵셔널을 unwrapping 할 수 있다.

! 를 사용하여 옵셔널을 해제하다 nil이 있는 경우 런타임 에러가 발생하게 된다.

 

예시에 적용해 생각해 보자

매우 좋은 호텔이라 호텔매니저들이 아침에 각 객실을 돌면서 아침식사를 묻는다고 가정하자

! 를 이용하여 강제적으로 unwrapping 하는 경우 호텔매니저들이 마스터키로 객실문을 강제적으로 열고 들어가 아침식사를 할 것인지 묻는 것과 같다.

 

?? 사용

?? 를 사용하여 unwrapping 할 수 있는데 공식적인(?) 해제 방식은 아니지만 가능하다 ㅎ..;

var name: String? = "JJ"
var num: Int?

print("name= \(name ?? "None")")
// 출력
// name= JJ

print("num= \(num ?? 5)")
// 출력
// num= 5

?? 를 사용하는 경우 임시적으로 옵셔널 해제가 가능하다.

임시 default값을 설정하는 느낌이다.

 

예시를 적용하면

위 예시 상황과 같은 상황이라 생각하자

호텔매니저들이 객실 문을 두들겨 사람이 없는지 확인하고 없는 경우 아침식사를 하지 않는다고 생각하는 것과 같다.

 

Optional Binding

가장 안전하게 옵셔널을 해제하는 좋은 방법이다.

예시를 적용해 보면

호텔매니저가 객실을 직접 방문하지 않고 전화를 이용하여 아침식사를 할 것인지 물어보는 것과 같다.

(단 손님이 방에 있는 경우 전화를 받는다고 가정한다.)

if-let

var name: String? = "hello"

if let unwrapName = name {
	// name이 옵셔널이 아닌 경우 실행되는 코드
}
else{
	// name = nil인 경우 실행되는 코드
}

if-let은 unwrapping을 한 값을 if문 밖에서 사용할 수 없다는 점이 가장 아쉽다..

 

guard-let

var name: String? = "hello"

guard let unwrapName = name else {
	// name = nil인 경우 실행되는 코드
}

guard-let은 unwrapping이후 guard구문 밖에서 unwrapName을 사용할 수 있다.

 

Optional Chaining

참고한 사이트 5번째 링크의 예시이다.

//Family 클래서 내부에 Child 옵셔널타입 변수가 존재한다.
class Family {
    var child: Child?
}

//Child클래서 내부에 String 옵셔널타입 변수가 존재한다.
class Child {
    var name: String?
}

//Family 클래스 인스턴스 참조
//family 인스턴스의 child 프로퍼티는 옵셔널타입 이므로 child뒤에 ? 를 표시한다.
var family = Family()
family.child?.name = "c"

let name = family.child?.name
print(name) //출력: nil
/*
family.child?.name 프로퍼티가 "벨류값"로 할당 되었더라도 
family의 child 프로퍼티는 옵셔널 타입 이기때문에 nil 이 출력된다.
*/

클래스 내부에 여러 개의 옵셔널이 있지만 매번 옵셔널을 확인하고 unwrap 하기 번거로운 과정을 생략하고 나온 방법이다.

family.child?. name 체인처럼 엮여 있어서 옵셔널 체이닝이라고 하는 것 같다.

Child클래스 내부의 name 변수 값이 확실해도 Family의 Child는 옵셔널 타입이므로 nil값이 출력된다.

 

위 문제를 해결하기 위해서 클래스를 생성하여 참조시켜야 한다.

 

class Family {
    var child: Child?
}


class Child {
    var name: String?
}

var family = Family()
var ch = Child()
ch.name = "a"
family.child = ch

bindingOpional()

func bindingOpional(){
    guard let n1 = family.child?.name else { print("값 없음")
        return
    }
    
    print(n1)	//	a 출력
}

ch 변수에 Child클래스의 인스턴스를 생성한다.

family 변수에 Family클래스의 인스턴스를 생성한다.

family.child를 Child클래스의 인스턴스를 참조하게 하면 값을 대입하여 사용할 수 있다.

family.child = ch

ch 인스턴스를 대입해서 family의 child는 옵셔널 타입이므로 옵셔널을 해제해야 값을 볼 수 있다.

위와 같이 옵셔널 바인딩을 사용하여 해제하면 값을 볼 수 있다.

 

Optional Chaining은 살짝 헷갈리네..

 

 

# 참고한 사이트

  1. https://medium.com/@codenamehong/swift-optional-1-54ae4d37ee09
  2. https://jud00.tistory.com/entry/%EC%98%A4%EB%8A%98%EC%9D%98-Swift-%EC%A7%80%EC%8B%9D-Optional%EC%9D%B4%EB%9E%80-%F0%9F%A4%94
  3. https://lxxyeon.tistory.com/152
  4. https://beingdesigner.tistory.com/36
  5. https://zetal.tistory.com/entry/swift-%EA%B8%B0%EC%B4%88%EB%AC%B8%EB%B2%95-4-%EC%98%B5%EC%85%94%EB%84%90

'iOS > Swift 상식' 카테고리의 다른 글

Instance Method, Type Method  (0) 2023.02.04
Swift - String의 SubScript 접근  (0) 2023.01.23
Swift - Any vs AnyObject  (0) 2023.01.23
Swift - Convenience init  (0) 2023.01.23
Swift - Copy On Write(COW) 동작 방식  (0) 2023.01.23