옵셔널이란? 다른 언어들에서 사용하는 null이나 nil을 이야기 하는것이다.
java의 경우 일반 변수나 배열에다 = null으로 null을 지정해 줄 수 있지만
swift의 경우 nil이 허용되는 데이터는 따로 명시를 해주고 사용해야 하며 이러한 것을 옵셔널타입이라고 한다.
이러한 옵셔널은 옵셔널이 아닌 요소들처럼 바로 사용하는것은 불가능하고 따로 처리를 해 주어야 사용이 가능하다( 이를 옵셔널 바인딩이라고 함)
var someData: String?
var someData2: String? = nil
var someData3: String = nil // 에러 발생
변수 이름뒤에, 타입이 지정되어 있다면 타입 뒤에 ?물음표를 붙이는것으로 옵셔널임을 나타낼 수 있다.
옵셔널이 아닌 데이터에 nil을 대입하는것은 불가능
옵셔널은 == 으로 nil과 비교할 수 있다
당연히 ><을 이용한 크기 비교는 안되고 == nil 혹은 != nil 같은 값이 존재하는지에 대한 비교연산만 가능하다
var someData: String?
if someData != nil{
print("someData is not empty")
}else{
print("someData is empty")
}
//결과....."someData is empty"
var someData: String? = nil
print(someData) // 오류발생
옵셔널 데이터의 경우 그냥 사용할 수 없다.
swift의 경우 옵셔널(null데이터)을 엄격하게 관리하는 편이기 때문에 옵셔널데이터를 추출하는 별도의 과정이 필요하다.
if let 상수 이름 = 추출하고자 하는 옵셔널 변수/상수{
print(상수 이름)
}else {
print("데이터가 비었습니다.")
}
위와 같은 형태로 데이터를 추출해서 사용하는것이 제일 안전하다.
let myNumber? = 123
if let myNumber = myNumber {
print("My number is \(myNumber)")
}
//결과....."My number is 123"
따로 값에 접근한 후에 기존 옵셔널 상수 또는 변수를 참조할 필요가 없다면 같은 이름으로 새로운 상수 또는 변수를 사용할 수 있다
let myNumber? = 123
if let myNumber{
print("My number is \(myNumber)")
}
//결과....."My number is 123"
이전의 코드처럼 변수/상수의 이름이 다르게 설정할 필요가 없는경우 이렇게 생략해서 사용하는것도 가능하다.
let myNumber1? = 123
let myNumber2? = 456
let myNumber3? = 789
if let myNumber1, let myNumber2, let myNumber3{
print("My number is \(myNumber1), \(myNumber2), \(myNumber3)")
}
//결과....."My number is 123, 456, 789"
,로 구분해서 여러개의 데이터를 바인딩하는것이 가능하다.
옵셔널 바인딩의 경우 if-let이 아니라 guard-let을 사용할 수 있다.
guard란 함수에서 조기종료를 위해 사용하는 if문 이라고 생각하면 된다.
guard를 만족하지 못하면 바로 else에서 오류를 발생시키거나 함수를 종료하는 코드를 작성하는 형태라고 생각하면 된다.
guard let의 경우 if let과는 다르게 조건문 외부에서도 사용이 가능하다.
var opt:Stirng? = "hello"
if let hello = opt else{
print("opt is empty")
}
print(hello) // 에러
if let의 경우 if문 밖에서 사용하는것이 불가능 하다.
var opt:Stirng? = "hello"
var hello: String
if let opt{
hello = opt
}else{
print("opt is empty")
}
print(hello)
//결과.....hello
if let으로 바인딩한 데이터를 if외부에서 사용하려면 별도의 변수나 상수에 대입을 한 다음에 사용해야 한다.
하지만 guard let은 다르다
var opt:Stirng? = "hello"
func someFunction(){
guard let hello = name else{
return nil
}
print(hello)
}
someFunction()
//결과.....hello
이처럼 guard let으로 바인딩 한 데이터는 그 이름 그대로 함수 내부에서 사용이 가능하다.
이것이 if-let과의 큰 차이점이다.
if-let 또는 guard-let의 형태로 옵셔널 바인딩을 했지만 if-var, guard-var의 형태로 옵셔널 바인딩하는것도 가능하다.
차이점으로는 바인딩한 데이터가 변수가 되어 수정하는것이 가능해 진다.
//if-var 예시
var optionalNumber: Int? = 10
if var number = optionalNumber {
number += 5
optionalNumber = number
}
//guard-var 예시
func updateName(name: String?) {
guard var validName = name else {
print("이름이 없습니다.")
return
}
// 변수 값 수정
validName = validName.uppercased()
print("업데이트된 이름: \(validName)")
}
let으로 바인딩 하게 되면 위와 같은 형태는 불가능하다.
옵셔널 바인딩이 많이 쓰이는 형대이고 if-else로 데이터를 경우에 따라 상세하게 처리하기 때문에 옵셔널 바인딩이 좋은 방법이긴 하지만
간단하게 사용하고 싶을땐 코드길이가 길어서 조금 불편할 수 있다 그럴땐 대체값을 지정하는 키워드를 사용하면 된다.
?? 키워드
let name: String? = nil
let greeting = "Hello, " + (name ?? "friend") + "!"
print(greeting)
//결과.....Hello, friend!
이처럼 옵셔널데이터 ?? 대체값 의 형태로 작성하면 nil이 아닌경우 그대로 옵셔널데이터를 사용 할 수 있고 내부에 데이터가 없는 nil인 경우 대체값을 반환하게 된다.
언래핑이란 말그대로 un wrapping 무언가를 열다 라는 뜻으로 해석된다.
앞서 if-let이나 ?? 으로 옵셔널 데이터를 처리한것을 말한다. 이 경우 데이터가 nil인 경우를 확실히 처리한다.
강제 언래핑의 경우 nil인 경우를 따로 처리하지 않고 옵셔널 데이터를 처리하는 방식을 말한다.(말그대로 강제로 사용하는 것)
만약 nil인 데이터를 강제 언래핑 하는경우 컴파일 단계에서는 오류가 없지만 런타임 단계에서 오류가 발생한다.
그냥 옵셔널 타입의 데이터 이름뒤에 !느낌표를 붙여서 사용하면 된다.
let name: String? = "gil-dong"
let oname: String? = nil
print(name!)
print(oname!) // 런타임 에러
사용하기 간편하지만 앞서 말했던 것처럼 nil인 데이터를 강제 언래핑 하는것은 위험하다.
왠만하면 사용하면 안된다 진짜 nil이 안들어 갈 데이터라는 확신이 있거나 급하게 구현만 하는게 필요한 경우가 아니라면 사용하지 않는것을 권장한다.
프로그램을 작성하다 보면, 옵셔널이 한 번 값이 설정된 이후에는 계속해서 값이 존재할 것이라고 확신할 수 있는 경우가 있다.
이런 상황에서는 매번 옵셔널을 확인하고 언래핑(Unwrapping)하는 과정이 번거로울 수 있다.
이를 해결하기 위해 암시적으로 언래핑된 옵셔널을 사용할 수 있다. 옵셔널이지만, 사용 시에 자동으로 언래핑되어 일반 값처럼 사용할 수 있는 옵셔널이다.
var someName: String! = "gildong"
옵셔널을 선언할때 ?를 사용하던 곳에 !느낌표를 작성해주면 된다.
이렇게 암시적으로 언래핑된 옵셔널은 별도의 바인딩없이 일반 변수처럼 사용이 가능하다.
하지만 다른 옵셔널과 마찬가지로 nil이 들어있는경우 데이터를 사용하려고 하면 런타임 단계에서 오류가 발생한다.
물론 위에서 했던 것처럼 if-let을 이용한 옵셔널 바인딩이나 ??을 이용한 대체값을 이용한 안전한 언래핑도 가능하다.