[Swift] Optional

Byunghoon Lee·2021년 8월 18일
0

iOS

목록 보기
9/11
post-thumbnail

Optional

이 변수에는 값이 있을수도, 없을수도 있어(nil).

Optional은 '?'을 통해 표현되며,
nil을 표현하기 위한 수단으로 (?)물음표를 사용합니다.
기본적인 형태는 아래와 같습니다.

let a: Int?
print(type(of: a))
// Optional<Int>

위 처럼 type을 print 해보면 type이 Optional포장되어(wrapping) 있습니다.

이해를 돕기 위해 예시로,

let game: String? = "OverWatch"
var favoriteGame = "좋아하는 게임은 \(game) 입니다."
print("\(favoriteGame)")
// 좋아하는 게임은 Optional("OverWatch") 입니다.

좋아하는게임은 Optional("OverWatch")가 됩니다. 😨

이처럼 포장되어있는 Optional을 제거하려면 언박싱(unwrapping)을 해줘야 합니다. 우리는 이 작업을 (Optional Unwrapping) 이라고 합니다.
Unwrapping을 알아보기전 아래의 내용은 중요하니 꼭 읽어봅시다.

들어가기전

아래에 print는 어떻게 나올까요??

let game: String? = nil
print(game)

오답 : Optional("nil")
정답 : nil

왜??

Optional은 을 감쌉니다.
그말은 즉, nil은 값이 없음 을 나타내기 때문에 Optional("nil")이 아닌 nil이 나오게 된것입니다.

다른말로 nil은 값이 없기 때문에 Unwrapping을 하게되면 에러가 발생 합니다.

Unwrapping

위의 예제와 같이 출력값이 Optional("OverWatch") 이렇게 나오는걸 원하진 않을겁니다.
올바른 출력값을 위해 Unwrapping 하는 방법은 아래와 같습니다.

1. Forced Unwrapping
2. Optional Binding

1. Forced Unwrapping (강제 추출)

먼저 !(느낌표) 를 사용해서 unwrapping하는 방법.

아래 예시를 보자

let game: String? = "OverWatch"
var favoriteGame = "좋아하는 게임은 \(game!) 입니다."
print("\(favoriteGame)")

// 좋아하는 게임은 OverWatch 입니다.

Optional이 없어지고 자연스럽게 문장이 Print 되는걸 볼 수 있지만!!

※ 이 경우는 주의할 점이 있습니다.

무조건 변수가 있는 상황에서만 사용 !!!

강제로 unwrap 하는것이기 때문에, 값이 없는 nil일 경우에도 옵셔널을 해제하려고 해서 잘못 사용하다간 컴파일 오류나, 비정상 종료가 생길수 있습니다.

2. Optional Binding

Optional Binding은 Optional 타입으로 선언된 변수에 값이 있는지 없는지를 확인할 수 있도록 해주는 기능입니다.
강제 추출 방법에 사용했던 !(느낌표) 를 사용하지않고 Optional 타입의 변수 값을 출력할 수 있어서 좀 더 안전한 형태로 값을 얻을 수 있습니다.
가장 많이 쓰이는 방법은 아래와 같습니다.

  • if let
  • guard
// if let
if let name: Type = OptionalExpression {

}

// guard
guard let name: Type = OptionalExpression else {

}

공통점

  1. 옵셔널 표현식을 먼저 평가한다.
  2. 값이 있는 경우 정의된 상수(name)에 옵셔널이 해제된 값을 저장하고 true를 반환한다.
  3. nil인 경우 false를 반환한다.
  4. 타입 추론이 가능하여 Type Annotation은 대부분 생략한다.

if let

먼저 if let을 이용한 방법.

예시로 보면 이해에 도움이 될것 같습니다.

// 값이 있을경우 
let testNumber: Int? = 123

// 값이 없을경우
let testNumber: Int? = nil

if let actualNumber = testNumber { // testNumber에 값이 있을 경우
    print("\(testNumber)은 실제로 \(actualNumber)입니다.")
} else { // testNumber 가 nil일 경우
    print("\(testNumber)는 변환될 수 없습니다.")
}
// 출력 값
// 값이 있을경우: Optional(123)은 실제로 123입니다.
// 값이 nil일 경우: nil는 변환될 수 없습니다.

코드를 보면 알수 있듯,
testNumber에 값이 있으면, 해당 Optional값을 unwrapping 하여 actualNumber에 123만 대입할 수 있게 합니다.

반대로, testNumber의 값이 nil일 경우엔 false를 반환하게 되어 else 구문의 코드를 실행하게 됩니다.

※ 만약 위 예시 코드에서 else 구문이 없다면?

  • 아무것도 나오지 않습니다.

※ Tip

한번에 여러 개의 옵셔널 타입을 바인딩 할 수도 있습니다.

let game: String? = nil
let company: String? = nil

// Optional Type과  Non Optional Type의 이름은 똑같이 써도 상관없음.
if let game: game, let company = company { 

}

다만, game, company 모두 값이 있어야 if 구문이 true가 됩니다.

추가로, 아래처럼 조건문도 두 가지 방법으로 나타낼 수 있습니다.

var age: Int? = 20

// 1. 조건식
if let actualAge = age {
  if actualAge >= 20  {
    print("군대가야지")
  } else {
    print("수능봐야지")
  }
}
// 군대가야지

// 2. ,(쉼표) 사용
var age: Int? = 20

if let actualAge = age, actualAge >= 20 {
    print("군대가야지")
} else {
    print("수능봐야지")
}
// 군대가야지

, (쉼표)를 사용하여 &&의 효과도 줄수 있습니다.

guard let

guard 영어 그대로 지킨다는 뜻으로 기본 형태는 아래와 같습니다.

func guardFunc() {
guard let nonOptionalNum = optionalNum else {
  // `optionalNum`이 nil 일 때
  return // 함수 밖으로 나가!
  }
  // `optionalNum`이 nil이 아닐 때
}

guardFunc()

guard문은 특성상 함수(메서드)에서만 쓰이며,
guard 구문의 조건을 만족하지 못하면 else문으로 보내 함수 밖으로 퇴출 (return) 시킨다.

아래 예시 코드로 한번 보자.


func guardNum(_ num: Int?){
    let optionalNum: Int? = 777
 // let optionalNum: Int? = nil // 주석 해제 후 위 내용과 비교 해보자.
    guard let checkNum = num else {
        print("이건 nil이야")
        return
    }
    print("이건 checkNum이야: \(checkNum)")
}
guardNum(optionalNum)
// 이건 checkNum이야: 777

if let 과 비교하면 true, false일 때 위치가 조금 다릅니다.
if let의 경우 if문 안에서,
guard문은 guard문 밖에서 사용 가능합니다.

3. Nil-coalescing

coalescing은 "하나로 합치다" 라는 뜻으로, nil-coalescing 또한 언래핑(unwrapping)하는 방법 중 하나 입니다.

?? 를 기준으로 좌측에는 옵셔널에 값이 있으면 적용될 값을, 우측에는 값이 없을때 적용될 값을 넣으면 됩니다..

아래 예시를 보자

var haveMyWay: String? = nil
print("사장님이 회식하자시는데 짜장면 드신댄다 내선택은? :" , haveMyWay  ?? "사장님 따라 짜장면...") 

// "사장님이 회식하자시는데 짜장면 드신댄다 내선택은? : 사장님 따라 짜장면..."

var haveMyWay: String? = "응~ 꿔바로우 깐풍기 팔보채 고량주 ~~~"
print("사장님이 회식하자시는데 짜장면 드신댄다 내선택은?" , haveMyWay  ?? "사장님 따라 짜장면...")

// "사장님이 회식하자시는데 짜장면 드신댄다 내선택은? 응~ 꿔바로우 깐풍기 팔보채 고량주 ~~~"

조건이 많지 않고, 코드를 간결하게 만들수 있어 가독성에 좋을것 같다.

참고

devapploper
babbab2
codenamehong

profile
Never never never give up!

0개의 댓글