값을 저장하려는 경우 정확한 값을 알지 못했을 때 Int
라면 0을 String
이라면 ""
을 사용할 수 있지만 값이 없다는 것과는 다른 의미입니다.
스위프트에서는 값이 없다는 것을 표현하기 위해 옵셔널을 사용합니다.
nil
을 사용합니다.var age: Int? = nil // nil
age = 38 // 38
스위프트에서는 왜 옵셔널이 있는 걸까?
옵셔널은 스위프트의 모든 타입에 사용할 수 있습니다.
옵셔널을 사용하여 값이 없다는 것을 나타낼 수 있습니다. 컴파일러에게 값의 정보를 알려줄 수 있어서 컴파일 중에 최대한 많은 버그를 잡아낼 수 있습니다.
변수나 상수를 옵셔널타입으로 선언하면 값에 접근할 때 nil인상태에서 접근하면 에러가 발생할 것입니다. 따라서 이러한 처리를 하기위해 언래핑을 사용합니다.
옵셔널타입만 언래핑을 사용합니다.
언래핑을 하는 방법 중 하나는 if let
구문을 사용합니다
if let
값이 있는 경우 동작할 코드 else
값이 없는 경우(nil) 동작할 코드
var name: String? = nil
if let unwrapped = name {
print("\(unwrapped.count) letters")
} else {
print("Missing name.")
}
옵셔널값을 사용하면 언래핑을 사용해야 하는 이유?
변수나 상수를 옵셔널타입으로 정의하면 값들은 옵셔널로 래핑되어서 저장됩니다.
스위프트는 옵셔널값을 언래핑하지 않고서는 사용할 수 없게 만들었습니다. 예를들어 값이 없는경우인데 그 값을 활용하는 경우 여러 곳에서 에러가 발생할 수 있기 때문입니다.)
옵셔널을 언래핑하는 또 다른 방법은 guard let
구문이 있습니다.
값이 nil
이라면 현재 함수, 루프 또는 조건을 종료합니다.
if let
과 guard let
의 가장 큰 차이점은 해제한 시점에서 블록 내부에서 작동하는지 블록외부에서 작동할 수 있는 지 여부입니다.
func greet(_ name: String?) {
guard let unwrapped = name else {
print("You didn't provide a name!")
return
}
print("Hello, \(unwrapped)!")
}
🌟 if let
과 guard let
의 속도차이가 있을까 ? → 없다.
값이 nil
이 아닌 것을 확신하는 경우에 옵셔널을 강제로 언래핑을 할 수 있습니다.
사용방법은 !
기호를 붙여줍니다.
let str: String = "5"
let num = Int(str)!
nil
인 값을 강제언래핑하는 경우에 코드 crash가 발생할 것입니다. 따라서 값이 있다고 확신되는 경우에만 사용합니다.암묵적으로 언래핑된 옵셔널은 값을 포함하거나 nil을 가질 수 있습니다. 그러나 일반 옵셔널과 달리 래핑을 풀 필요가 없습니다.
옵셔널을 해제하지 않고 nil을 저장하여도 코드가 충돌하지 않습니다. 하지만 nil인 경우 접근하면 코드가 충돌합니다.
var age: Int! = 4 // 4
age = 5 // 5
age = nil // nil
nil병합연산자는 옵셔널을 풀고 값이 있으면 내부 값을 반환하고 nil이라면 기본값을 반환합니다.
사용방법은 옵셔널 값 뒤에 ??
문자를 추가한후 기본값을 설정합니다.
func username(for id: Int) -> String? {
if id == 1 {
return "Taylor Swift"
} else {
return nil
}
}
let user: String = username(for: 15) ?? "Anonymous" // Anonymous
let user1: String = username(for: 1) ?? "Anonymous" // Taylor Swift
강제 언래핑을 대체로써 옵셔널 체이닝을 사용합니다. 강제언래핑은 값이 없는 경우 런타임에러가 발생하지만 옵셔널체이닝을 사용하면 런타임에러 대신 nil
이 반환됩니다.
옵셔널체이닝 사용방법은 ?
을 붙여줍니다.
// MARK: - names배열에 first가 있는 경우
let names = ["John", "Paul", "George", "Ringo"]
let beatle = names.first?.uppercased() // "JOHN"
// MARK: - names배열에 first가 없는 경우
let names: [String] = []
let beatle = names.first?.uppercased() // nil
?
를 써서 옵셔널체인을 활성화할 수 있습니다. a.b?.c옵셔널체이닝이 중요한 이유
옵셔널체이닝을 사용하면 한줄의 코드에서 여러 옵션계층을 파헤칠 수 있으며, 이러한 계층 중 하나라도 nil이라면 전체 줄이 nil이 됩니다.
let names = ["Vincent": "van Gogh", "Pablo": "Picasso", "Claude": "Monet"]
let surnameLetter = names["Vincent"]?.first?.uppercased()
에러처리를 하는 구문 do ~ catch , try 구문이 있는데 try?을 사용하면 던지는 함수를 옵셔널을 반환하는 함수로 변경합니다. 함수에서 오류가 발생하면 nil로 반환하고, 그렇지 않으면 반환값이 옵셔널래핑된 값을 반환합니다.
enum PasswordError: Error {
case obvious
}
func checkPassword(_ password: String) throws -> Bool {
if password == "password" {
throw PasswordError.obvious
}
return true
}
// MARK: - try 사용한 경우
do {
try checkPassword("password")
print("That password is good!")
} catch {
print("You can't use that password.")
}
// MARK: - try? 사용한 경우
if let result = try? checkPassword("password") {
print("Result was \(result)")
} else {
print("D'oh.")
}
// 실행결과
D' oh
이니셜라이저를 사용할 때 작동하거나 작동하지 않을 수 있는 실패할 수 있는 이니셜라이저가 있습니다.
이니셜라이저 시점에 객체생성에 대한 에러를 미리 처리할 수 있습니다.
사용방법은 init?()
을 사용합니다. nil
이라면 반환할 수 있습니다.
struct Person {
var id: String
init?(id: String) {
if id.count == 9 {
self.id = id
} else {
return nil
}
}
}
let person = Person(id: "123456")
let person1 = Person(id: "123456789")
print(person?.id)
print(person1?.id)
// 실행결과
nil
Optional("123456789")
sender as? UISwitch
결과값은 옵셔널타입sender as! UISwitch
결과값은 일반타입class Animal { }
class Fish: Animal { }
class Dog: Animal {
func makeNoise() {
print("Woof!")
}
}
let pets: [Animal] = [Fish(), Dog(), Fish(), Dog()]
for pet in pets {
if let dog = pet as? Dog {
dog.makeNoise()
}
}