
23-04-24
만약 옵셔널에 값이 할당되었다면, 해당 값이 옵셔널 내에서 래핑되었다(Wrapped)고 한다.
옵셔널 안에 래핑된 값을 사용할 때는 강제 추출 또는 강제 언래핑(Forced unwrapping)을 한다.
If문을 사용해 옵셔널과 nil을 비교하여 옵셔널의 값 소유 여부를 알아낼 수 있다. 논리연산자 '=='와 '!=' 를 사용해 이를 확인할 수 있다. 만약 옵셔널이 값을 가지고 있다면, nil과 '같지 않음'을 반환할 것이다.
최근 강제추출(Forced Unwrapping)은 무조건 추출(Unconditional Binding)으로 씀
if convertedNum != nil {
print("convertedNum contains some integer value.")
}
// Prints "convertedNum contains some integer value."
만약 옵셔널이 값을 가지고 있는 것을 명백히 알고 있다면, 그 옵셔널의 이름 뒤에 느낌표(!) 를 붙여서 그 값에 접근할 수 있다. 느낌표는 "나 여기 값 있는거 아니까 나타내줘." 라는 뜻으로 사용된다. 이를 옵셔널 값의 강제추출(Forced Unwrapping) 이라고 한다.
if convertedNum ~= nil {
print("convertedNum has an integer value of \(convertedNum!).")
}
// Prints "convertedNum has an integer value of 123."
값이 존재하지 않는 옵셔널에 !를 사용하면 런타임 에러가 뜨니, !를 사용하여 강제추출하기 전에는 옵셔널에 값이 있는지 확인하도록 하자.
반대로 앞의 경우에서 !를 뺀다면 다음과 같은 오류가 난다: "value of optional type 'Int?' must be unwrapped to a value of type 'Int'
!를 사용하지 않고 옵셔널을 언래핑 할 수 있다.
옵셔널이 값을 가지고 있는지 확인하기 위해 옵셔널 바인딩을 사용할 수 있고, 그 값을 사용 가능하게 만들 수 있다. if와 while문을 이용해 옵셔널 안의 값을 체크하고 이를 추출할 수 있다.
if문을 사용한 옵셔널 바인딩은 다음과 같이 작성한다.
if let <\constantName\> = <\someOptional\> {
<\statements\>
}
옵셔널 바인딩을 이용하면 possibleNum 예제를 다음과 같이도 표현할 수 있다.
if let actualNum = Int(possibleNum) {
print("The string \"\(possibleNum)\" has an integer value of \(actualNum)")
} else {
print("The string \"\(possibleNum)\" couldn't be converted to an integer")
}
// Prints "The string "123" has an integer value of 123"
이 코드는 다음과 같은 뜻이다 : 옵셔널인 possibleNum이 값을 가지고 있다면 actualNum에 할당해라.
옵셔널 안의 값이 이미 initialized 됐기 때문에 !를 사용해 값에 접근하려 하지 않아도 된다.
let myNum = Int(possibleNum)
// 여기서, myNum은 옵셔널 정수이다.
if let myNum = myNum {
// 여기서, myNum은 옵셔널이 아닌 정수이다
print("My number is \(myNum)")
}
// "My number is 123"
위 코드에서 두번째 줄에서 선언된 상수 myNum은 첫번째 줄의 옵셔널 myNum과는 다른 상수로, if문 안에서만 작동한다.
위와 같이 아무 이름이나 지정하여 언래핑 할 수 있다.
if let myNum {
print("My number is \(myNum)")
}
// Prints "My number is 123"
상수와 변수 모두 옵셔널 바인딩을 사용할 수 있다. if문 첫째줄의 myNum 값을 복제하고 싶다면 if var myNum 이라고 작성할 수 있고, 변수로서 사용할 수 있다. if문 안의 myNum에 가해지는 변경들은 래핑을 푼 기존 myNum이 아닌 그 지역변수에만 적용된다.
if문 안에서 옵셔널 바인딩으로 생성된 상수와 변수들은 if문 안의 body에서만 사용된다. 이와 반대로, gurad문으로 생성된 상수와 변수는 gurad문 이후에서도 사용할 수 있다.
필요한 최대한 많은 옵셔널 바인딩과 불 조건을 쉼표로 구분해 넣을 수 있다. 옵셔널 안의 아무 값이 nil이거나 불 값이 false인 경우, 그 if문 전체가 false로 간주된다.
if let firstNum = Int("4"), let secondNum = Int("42"), firstNum < secondNum && secondNum < 100 {
print("\(firstNum) < \(secondNum) < 100")
}
// Prints "4 < 42 < 100"
if let firstNum = Int("4"){
if let secondNum = Int("42") {
if firstNum < secondNum && secondNum < 100 {
print("\(firstNum) < \(secondNum) < 100")
}
}
}
// Prints "4 < 42 < 100"
&&연산자는 Bool 연산자를 피연산자로 사용하는 논리 연산자이다. 비교하는 두 값이 모두 Bool연산자일 때 AND의 역할을 수행함. 따라서 옵셔널 바인딩의 리턴값이 바로 &&로써 비교될 수 없어서 위와 같이 코드를 작성하는 것임.
콤마(,)는 조건문에서 한 조건 뒤에 다른 조건을 추가하는 용도로 사용된다.
서술했듯이, 상수와 변수 옵셔널은 "값이 없음"을 가질 수 있다. if문을 활용해 옵셔널이 값을 가지고 있는지 알아볼 수 있고, 값이 있다면 그 옵셔널의 값에 접근하기 위해 조건적으로 언래핑 될 수 있다.
프로그램 구조상 어떤 옵셔널이 항상 값을 가지고 있는 것이 명백한 경우들이 있다. 이런 경우들에서 어떤 옵셔널들이 항상 값을 가지고 있다고 가정할 수 있기 때문에, 모든 옵셔널에 접근할 때마다 그 값이 있는지 하나하나 확인할 필요가 없다.
이러한 옵셔널들을 '암묵적으로 언래핑된 옵셔널'이라고 부른다. 이런 옵셔널 뒤에는 물음표(String?) 대신 느낌표(String!)를 붙여서 표현한다. 옵셔널을 선언할 때, 그 옵셔널의 이름 뒤가 아닌 자료형의 뒤에 느낌표(!)를 붙여서 표현한다.
암묵적으로 언래핑된 옵셔널들은 옵셔널 값이 정의된 직후 존재하는 것이 확인되고, 이후 모든 부분에서 존재한다고 확실히 가정할 수 있을 때 유용하다. Swift에서 암묵적 언래핑된 옵셔널이 가장 처음 사용되는 것은 class initialization 중이다.
암묵적으로 언래핑된 옵셔널들은 매번 접근할 때마다 언랩할 필요가 없는 non-optional value처럼 사용될 수도 있다. 아래 예문은 wrapped 된 값에 접근할 때, 옵셔널 string과 암묵적으로 언래핑된 옵셔널의 차이점을 보여준다.
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 느낌표가 있어야 함
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 느낌표가 없어도 됨
암묵적 언래핑된 옵셔널을 필요할 때 옵셔널을 강제 언래핑 할 수 있는 허락을 구하는 것이라고 생각할 수 있다. 암묵적 언래핑 옵셔널을 사용할 때, Swift는 우선 그 옵셔널의 오리지널 값을 사용하려고 할 것이다; 만약 옵셔널로 사용되지 않는다면, Swift는 값을 강제 언래핑 한다. 위의 코드에서, 옵셔널 값인 assumedString은 implicitString이 명료하고 non-optional type String을 보유하고 있기 때문에, 그 값을 implicitString에 할당하기 전에 강제로 언래핑 된다.
let optionalString = assumedString
// The type of optionalString is "String?" and assuedString isn't force-unwrapped.
만약 암묵적 언래핑된 옵셔널이 nil이고 그 값에 접근하려고 한다면 런타임 에러가 발생한다. 이는 값이 없는 기본 옵셔널 뒤에 느낌표를 붙였을 때와 동일한 결과다.
기본 옵셔널을 체크하는 것과 같은 방법으로 암묵적 언래핑 옵셔널이 nil인지 여부를 체크할 수 있다:
if assumedString != nil {
print(assumedString!)
}
// Prints "An implicitly unwrapped optional string."
값을 체크하고 unwrap하기 위해 옵셔널 바인딩을 사용할 수도 있다.
if let definiteString = assumedString {
print(definiteString)
}
// Prints "An implicitly unwrapped optional string."
🌏 참고사이트
Swift Language Guide