Day 12 : 옵셔널
@ Day 12
# 옵셔널(Optionals)
값이 있을 수도 있고, 없을 수도 있는(nil)
타입을 나타내기 위해 사용- 타입 뒤에 물음표를 붙여
type?
과 같이 선언
var age: Int? = nil // 옵셔널 Int
var height: Int? = 200 // 옵셔널 Int
# 옵셔널 추출(Unwrapping optionals)
옵셔널은 값이 있을수도, 없을수도 있으므로 추출을 통해 값이 실제로 있는지 확인
필요
if-let 구문
값이 있으면 변수에 저장
, 없으면 다른 코드 실행
if let unwrapped = name { // 값이 있다면 unwrapped에 값이 저장됨
print("\(unwrapped.count) letters")
} else { // 값이 없다면
print("Missing name.")
}
guard-let 구문
옵셔널에 값이 없으면
추출을 시도한 함수 / 반복문 / 조건문을바로 탈출
하게 함if-let 구문
과 달리guard-let 구문
은 구문 이후에도추출된 옵셔널
사용 가능
func greet(_ name: String?) {
guard let unwrapped = name else {
print("You didn't provide a name!")
return
}
print("Hello, \(unwrapped)!") // 추출된 옵셔널 사용 가능
}
greet(nil) // You didn't provide a name
greet("sun") // Hello, sun!
# 강제 추출(Forced Unwrapping)
- 강제추출 시 옵셔널 타입을
비-옵셔널 타입으로 변환
할 수 있음 - 타입 뒤에
!
를 붙여서 사용 - 그러나 옵셔널에 값이 없는 경우 런타임 에러가 발생하므로 주의
let str = "5"
let num = Int(str)
print(num) // Optional(5)
let num2 = Int(str)! // 강제 추출
print(num2) // 5
# Implicitly Unwrapped Optionals
- 일반 옵셔널과 달리 암시적으로 언래핑된 옵셔널은
추출 과정이 불필요
- 대신 옵셔널에
값이 없는
경우런타임 에러
발생 - 타입 뒤에
!
를 붙여서 선언
let str1 = "5"
let str2 = nil
let age = Int(str1)! // 5
let age2 = Int(str2)! // KO : Error
# 닐 병합 연산(Nil Coalescing)
- 닐 병합 연산자
??
은 옵셔널을 추출해서값이 있으면 해당 값을 반환
하고값이 없으면 디폴트 값을 반환
func username(for id: Int) -> String? {
if id == 1 {
return "Taylor Swift"
} else {
return nil
}
}
let user = username(for: 15) ?? "Anonymous" // Anonymous
- 딕셔너리 키를 읽는 경우 항상 옵셔널을 반환하므로 닐 병합 연산 활용 가능
let scores = ["Sun": 100, "Moon": 80, "Star": 120]
let cookieRunScore = scores["Planet"] ?? 0 // nil coalescing
let cookieKingdomScore = scores["Planet", default: 0]
# 옵셔널 체이닝
- 코드에
옵셔널이 중첩되어 존재
할 때, 각각을 확인하여 하나라도nil
인 순간 그 이후는 무시되고, 전체가nil
이 됨
let names = ["Vincent": "van Gogh", "Pablo": "Picasso", "Claude": "Monet"]
let surnameLetter = names["Vincent"]?.first?.uppercased()
# 옵셔널 트라이 2가지
try?
throwing function
을옵셔널을 반환
하는 함수로 변환- 함수가 에러를 던지면
nil
이 반환되고, 정상 작동하면옵셔널 반환
enum PasswordError: Error {
case obvious
}
func checkPassword(_ password: String) throws -> Bool {
if password == "password" {
throw PasswordError.obvious
}
return true
}
if let result = try? checkPassword("password") { // 정상 작동
print("Result was \(result)")
} else { // 에러 발생
print("Too EZ")
}
try!
- 옵셔널 추출 과정을 생략할 수 있으나, 함수가 에러를 던지는 경우 런타임 에러 발생
enum PasswordError: Error {
case obvious
}
func checkPassword(_ password: String) throws -> Bool {
if password == "password" {
throw PasswordError.obvious
}
return true
}
try! checkPassword("sekrit")
print("OK")
# Failable Initializers
- 성공할 수도, 실패할 수도 있는 이니셜라이저를
failable initializer
라고 함 init?()
을 통해 선언- 초기화를 실패하는 경우 반드시
nil
을 리턴하도록 해야 함
struct Person {
var id: String
init?(id: String) {
if id.count == 9 {
self.id = id
} else { // 초기화 실패 시
return nil
}
}
}
var man1 = Person(id: "123456789")
man1?.id // 123456789
var man2 = Person(id: "") // nil
man2?.id // nil
# 타입캐스팅(Typecasting)
as?
키워드를 통해 클래스의 인스턴스를 부모 혹은 자식 클래스의 타입으로 사용할 수 있는 지 확인 가능- 사용할 수 있다면
해당 타입
(으로 변환 해) 리턴하고 없다면nil
리턴
class Animal {}
class Fish: Animal {}
class Dog: Animal {
func makeNoise() {
print("Woof!)
}
}
let pets = [Fish(), Dog(), Fish(), Dog()]
// Fish 클래스와 Dog 클래스 모두 Animal 클래스를 상속하므로 Swift는 pets가 Animal 배열 타입이라고 추론
// 따라서 Animal 클래스의 프로퍼티/메서드에만 접근 가능하므로 Dog 클래스의 makeNoise()를 호출하려면 타입캐스팅 필요
for pet in pets {
if let dog = pet as? Dog { // 타입 캐스팅
dog.makeNoise()
}
}
☀️ 느낀점
- 스위프트는
nil
을 처리하기 위한 방법이 매우 다양하다는 생각을 했다...어렵다...ㅋㅎㅋㅎㅋㅎ - 오늘부로 문법파트가 끝났는데 사실 벌써 가물가물한 부분도 많고 아직 직접 프로그램을 짜본 게 아니라 개념이 알쏭달송하게 느껴진다...내일 복습하면서 강의를 병행할 지 고민해봐야겠다!
Author And Source
이 문제에 관하여(Day 12 : 옵셔널), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@sunnysideup/Day-12-옵셔널저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)