Siwft) Optionals

5 minute read

Optionals

옵셔널은 swift의 Safe(안정성)에 해당하는 특징이다.

옵셔널은 값이 없는 경우에 사용한다. 옵셔널은 값이 있을 수도 있고 없을 수도 있는

두가지의 가능성을 가지고 있다.

Swift에서 옵셔널을 사용하면 특별한 상수 없이도 모든 타입에 대한 값이 전혀 없음을 나타낼 수 있다.

예제를 통해 알아보자.

Swift의 Int타입에는 String 타입을 Int 값으로 변환하는 이니셜라이저가 있다.

그러나 모든 문자열을 정수로 변환할 수 있는 것은 아니다.

문자열 “123”은 숫자 값 123으로 변환할 수 있지만 문자열 “Hello, world”는 변환할 숫자 값이 없다.

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// 변환된 숫자는 Int? 또는 Optioanl Int로 추정된다.

이니셜라이저가 실패할 수 있으므로 Int가 아닌 optional Int를 반환한다.

옵셔널 Int는 Int로 작성되지 않고 Int?로 지정된다.

물음표는 포함하는 값이 옵셔널 임을 나타내며, 일부 Int 값을 포함할 수도 있고, 전혀 값을

포함하지 않을 수도 있다는 것을 의미한다.


nil

nil은 옵셔널 변수를 특정한 값을 지정하여 값이 없는 상태로 설정한다.

var serverResponseCode: Int? = 404 // 404
serverResponseCode = nil // 값이 없다
non-optional 상수 및 변수를 사용해서 nil을 사용할 수는 없다.
코드의 상수 또는 변수가 특정 조건에서 값이 없는 상태에서 작동해야하는 경우
항상 해당 타입의 옵셔널 값으로 선언해야한다.

기본 값을 지정하지 않고, 옵셔널 변수를 정의하는 경우 변수는 자동으로 nil으로 지정된다.

var surve yAnswer: String?
Swift의 nil은 Objective-C의 nil과 같지 않다.
Objective-C의 nil은 존재하지 않는 객체의 포인터이다.
Swift에서 nil은 포인터가 아니다. 특정 유형의 가치가 없는 것이다.
어떤 타입의 선택사항도 객체 타입뿐만 아니라 nil로 설정할 수 있다.

if문과 강제 언래핑 (if Statements and Forced Unwrapping)

if문을 사용하여 옵셔널과 nil을 비교하여 옵셔널에 값이 포함되어 있는지

여부를 확인할 수 있다.

“equal to” 연산자인(==) 또는 “not equal to” 연산자인 (!=)를 사용하여 비교한다.

옵셔널에 값이 있는 경우, “not equal to”으로 지정된다.

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)

if convertedNumber != nil {
  print("convertedNumber는 일부 정수 값을 포함합니다.")
}
// "convertedNumber는 일부 정수 값을 포함합니다."

옵셔널에 값이 포함되어 있다고 확신하면 옵셔널의 이름 끝에 !를 붙여 강제 언래핑 할 수 있다.

느낌표는 ‘이 옵셔널에 값이 있다는 것을 알고 있습니다. 그것을 사용하시오’라고 표현한다.

이것이 바로 강제 언래핑이다. (Forced Unwrapping)

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)

if convertedNumber != nil {
  print("변환된 숫자는 \(convertedNumber) 정수 값을 갖는다.")
}
// "변환된 숫자는 123 정수 값을 갖는다."

강제 언래핑을 사용하여 존재하지 않는 옵셔널 값에 접근하려고 하면

런타임 오류가 발생한다. 강제 언래핑을 사용하기 전에 항상 옵셔널에서 nil 값이 아닌

값을 포함하는지 확인하여 강제 언래핑을 사용하여야 한다.

옵셔널 바인딩(Optional Binding)

옵셔널 바인딩을 사용하여 옵셔널에 값이 포함되어 있는지 여부를 확인하고,

값이 포함된 경우 옵셔널 상수 또는 변수로 사용할 수 있도록 한다.

옵셔널 바인딩은 옵셔널 내에서 값을 확인하고 단일 작업의 일부로 상수 또는 변수로 추출하는데

if문 또는 while문과 함께 사용할 수 있다.

if let Name = someOptional {
    Statements
}

위에 강제 언래핑이에서 강제 언래핑이 아닌 옵셔널 바인딩을 사용할 수 있다.

let possibleNumber = "123"

if let actualNumber = Int(possibleNumber) {
  print("문자열\(possibleNumber)는 정수 값이 \(actualNumber)이다.")
} else {
  print("문자열\(possibleNumber)는 정수로 반환할 수 없다.")
}

// "문자열 "123"는 정수 값이 123이다."

위 코드를 해석하면 ‘Int(possibleNumber)에서 반환되는 옵셔널 Int에 값이 포함되어 있는 경우,

actualNumber이라는 새로운 상수를 옵셔널에 포함된 값으로 설정한다’로 표현할 수 있다.

변환에 성공하면 if 문의 첫 번째 분기 내에서 실제 Number 상수를 사용할 수 있다.

이미 옵셔널 내에 포함된 값으로 이니셜라이저 되었기에 !접미사를 사용하여 해당 값에 접근하지 않는다.


상수와 변수 모두 옵셔널 바인딩과 함께 사용할 수 있다.

만약 위 코드에서 actualNumber 상수의 값을 변경하고 싶다면

let 대신 if var actualNumber를 사용하면 된다. 그렇게되면 옵셔널 내에 포함된 값은

상수가 아닌 변수로 사용할 수 있다.

필요에 따라 쉼표로 구분하여 한 번에 여러 가지 옵셔널 바인딩 및 부울 조건을 사용할 수 있다.

옵셔널 바인딩의 값이 nil 이거나 부울 조건이 false로 된다면

if문의 전체 조건이 false로 간주된다.

if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
  print("\(firstNumber) < \(secondNumber) < 100")
}
// "4 < 42 < 100"

if let firstNumber = Int("4") {
  if let secondNumber = Int("42") {
    if firstNumber < secondNumber && secondNumber < 100 {
      print("\(firstNumber) < \(secondNumber) < 100")
    }
  }
}
// "4 < 42 < 100"
if문에서 옵셔널 바인딩으로 생성된 상수와 변수는 if문 내부에서만 사용 가능하다.
같은 의미로 guard문을 사용하여 생성된 상수와 변수는 if문과 마찬가지로 guard 코드 내에서 사용 가능하다.

암시적 옵셔널 언래핑(Implicitly Unwrapped Optionals)

위에서 말한대로 옵셔널에는 상수 또는 변수가 ‘값이 없음’을 사용할수 있다.

옵셔널에는 if문을 사용하여 값이 존재하는지 확인할 수 있으며, 옵셔널 값이 존재하는 경우

옵셔널 값에 지정할 수 있도록 옵셔널 바인딩으로 조건부로 언래핑을 할 수 있다.

때때로 프로그램 구조를 보면 옵셔널에는 값이 처음 설정된 후에 분명히 항상 값이 있다.

이러한 경우에는 항상 값이 있다고 안전하게 가정할 수 있으므로 접근할 때마다 옵셔널 값을

확인하고 언래핑 할 필요가 없다.

이러한 종류의 옵셔널은 암시적 옵셔널 언래핑으로 정의할 수 있다.

옵셔널로 만들려는 타입 뒤에 물음표가 아닌 느낌표를 배치하여 암시적으로 옵셔널 언래핑을 작성한다.

느낌표를 사용할 때 느낌표를 옵셔널 이름 뒤에 붙이지 않고, 옵셔널 타입 뒤에 느낌표를 배치한다.

암시적 옵셔널 언래핑은 옵셔널의 값이 처음 정의 된 직후에 존재하는 것으로 확인될 때 유용하며,

그 이후의 모든 지점에 분명이 존재하는 것으로 가정할 수 있다.

암시적 옵셔널 언래핑의 주된 용도는 클래스에서 초기화 할 때이다.

참고 주소

암시적 옵셔널 언래핑은 백그라운드에서 일반 옵셔널이지만 접근할 때마다 옵셔널 값을

언래핑 할 필요 없이 옵셔널 값이 아닌 것처럼 사용할 수도 있다.

let possibleString: String? = "옵셔널 문자열입니다."
let forcedString: String = possibleString! // 느낌표가 필요함

let assumedString: String! = "암시적 옵셔널 언래핑 문자열입니다."
let implicitString: String = assumedString // 느낌표 필요 없음

암시적 옵셔널 언래핑은 필요에 따라 겡제 언래핑을 부여하는 것으로 생각할 수 있다.

암시적 옵셔널 언래핑을 사용하려할 때 Swift는 먼저 일번적인 옵셔널 값으로 사용하려 한다.

옵셔널 값으로 사용할 수 없다면 Swift는 강제 언래핑으로 값을 푼다.

위 코드를 다시 보면 implicitString은 명시적이고 옵셔널 String 타입이 아니기 때문에

implicitString에 값을 할당하기 전에 강제 언래핑이 된다.

다음 코드는 optionalString에 명시적 타입이 없으므로 일반 옵셔널이다.

let optionalString = assumedString
// optionalString타입은 'String?'이며 assumedString은 강제 언래핑되지 않는다.

암시적 옵셔널 언래핑이 nil이고 래핑된 값에 접근하려고 하면 런타임 오류가 발생한다.

결과는 값이 포함되지 않은 일반 옵션 뒤에 느낌표를 붙이는 경우와 동일하다.

암시적 옵셔널 언래핑이 일반 옵셔널을 확인하는 것과 동일한 방법으로 nil을 확인할 수 있다.

let assumedString: String! = "암시적 옵셔널 언래핑 문자열입니다."

if assumedString != nil {
  print(assumedString!)
}
// "암시적 옵셔널 언래핑 문자열입니다."

또한 옵셔널 바인딩과 함께 암시적 옵셔널 언래핑을 사용하여 단일 문에서 값을 확인하고

래핑을 해제할 수 있다.

if let definiteString = assumedString {
  print(definiteString)
}
// "암시적 옵셔널 언래핑 문자열입니다."
변수가 나중에 nil이 될 가능성이 있을 경우 암시적 옵셔널 언래핑을 사용하면 안된다.
변수 Life Time 동안에 nil 값을 확인해야 하는 경우 항상 일반적인 옵셔널 타입을 사용한다.

Comments