[Swift] - 옵셔널(Optional)

longlivedrgn·2023년 1월 13일
0

swift문법

목록 보기
33/36
post-thumbnail

💥 옵서널(Optional)

  • nil을 사용할 수 있는 타입과 그렇지 못한 타입을 구분하기 위한 수단이다.
  • nil을 사용할 수 있는 타입을 Optional Type이라고 한다.
  • Optional Type은 타입 옆에 ?를 붙힌다.

→ 즉, 에러가 났지만 바로 앱을 중단시키는 것이 아닌, nil을 내 뱉을거다.(기회를 한번 더 주는 것!)

  • 어떠한 상황에서 필요한 지 예를 통해서 알아보자. 아래와 같은 경우는 우리가 예상하는대로이다.
let people = ["kim": 1, "lim": 2, "hyun": 3]

let name = people["kim"] // 1 
  • 그러나, 만약에 아래와 같이 접근을 한다면 에러가 나와야된다.(Optional이 아니라면) 이러할 경우, optional을 활용하여 error를 바로 던지는 게 아니라 nil을 던져 프로그램의 강제 종료를 막는 것이다!
let name = people["brrr"] // nil
  • return 값이 optional이다!

🌟 Optional을 선언하는 방법

🫧 Type anootation 활용

  • 아래와 같이 ?을 통하여 optional을 선언할 수 있다.
var people: String?
people = nil // 에러가 생기지 않는다. 

// 만약
var peopel: String
people = nil // error

🫧 Type inference 활용

  • 선언과 동시에 값을 초기화 해주면 된다.
let number: Int? = nil
let number2 = number
// number2도 옵셔널 타입이 된다.

  • 아래와 같이 number는 Optional(4)이다. 이걸 벗겨주는 방법을 알아보자.
let number: Int? = nil
print(type(of: number)) // Optional<Int>

number = 4
print(number) // Optional(4)

🌟 Optional Unwrapping

  • Optional을 때주는 방법이다
  • Unwrapping 하고자하는 변수나 상수가 nil이면 안된다.
  • Optional은 존재하지 않는다. nil은 어떠한 값이 아니다! 그냥 값이 없는 것이다.
  • 즉, nil은 unwrapping 할 수 없다. 만약 하려고 하면 에러가 난다!

🫧 1. Forced Unwrapping

  • 옵셔널에 값이 nil이건 말건 그냥 강제로 옵셔널을 때어버리는 것이다.
let number: Int? = nil
print(a!) // error

🫧 2. Optional Binding

  • 아래와 같은 로직를 통해서 옵셔널을 때준다.
    1. 만약 nil이 아니라면 상수에 옵셔널이 해제가 된 값을 저장하고, true를 return한다.
    2. 만약 nil이라면 false를 return 한다.

💫  if let

  • 아래와 같은 방식을 통하여 optional binding을 진행한다.
let optionalNum: Int? = 10

if let num = optionalNum { print("num") } else { print("optionalNum") }

// 10
  • 만약에 nil이 저장되어있었다면? nil을 프린트한다.
let optionalNum: Int? = nil

if let num = optionalNum { print("num") } else { print("optionalNum") }

// nil

💫 guard let

  • 아래와 같은 방식을 통하여 Optional Binding을 한다.
let optionalNum: Int? = 10

guard let num = optionalNum else { print("optionalNum") }

// 10

💫 if let VS guard let

  • 😇 if let
    • 먼저 바인딩된 상수의 scope가 서로 다르다.
    • 아래와 같이 if문에서 정의된 num은 if문 안에서만 사용이 가능하다.(else 문 안에서도 사용이 안된다) ⇒ 지역 변수의 성격
    • 단순한 optional 처리만 하고 피드백을 주고싶을 때!
let optionalNum: Int? = 10
if let num = optionalNum { print("num") } else { print("optionalNum") }
print(num) // error
  • 😇 guard let
    • 그러나, guard 문의 경우 guard 문 밖에서도 사용이 가능하다.(else 문에서는 안된다) ⇒ 전역 변수의 성격
    • 옵셔널 처리 값이 nil일 경우 무조건 함수의 실행을 종료시키고 싶을 때!
let optionalNum: Int? = 10
guard let num = optionalNum else { return }
print(num) // 10

🌟 IUO(Implicitly unwrapped optional) - 옵셔널 묵시적 추출

  • 일단 예시부터 봐보자! 아래는 IUO이다. 이것도 (강제추출을 하는)Optional type이다.
  • 그러나, non-optional type으로 처리가 되어야할 경우 값을 자동으로 뽑아내준다.
  • 즉, optional type을 non-optional type에 대입이 될 경우, 별도의 추출 과정이 없이 대입이 가능하다.
let name: String! // IUO
  • 위의 맨 마지막 줄에 주목해보자

optional typenon-optional type에 대입이 될 경우, 별도의 추출 과정이 없이 대입이 가능하다.

  • 우리가 알고 있는 Optional Type은 아래와 같다. Optional unwrapping 없이 대입을 하기에 error가 난다.
var name: String? = "Miro"
var name2: String = name // error
  • 그러나, IUO를 사용할 경우 아래와 같은 코드가 가능해진다. 그리고 name2nonOptional type인 변수가 된다!
var name: String! = "Miro" // 아직, Optional<String>이다
var name2: String = name // OK

🌟 옵셔널 체이닝(Optional Chaining)

  • 옵셔널은 연쇄적으로 사용하는 것이다!
  • .(점)을 통하여 메서드나 프로퍼티에 접근을 연쇄적으로 할 때, 옵셔널 값이 하나라도 있다면, 옵셔널 체이닝이다.
  • 아래의 두 개의 예 모두 옵셔널 체이닝이다.
human.gender?.bloodType
human?.gender?.bloodType
  • 예를 통하여 옵셔널 체이닝을 이해해보자. 두 struct를 먼저 정의해보자.
struct Genders {
	var bloodType: String
	var height: Int
}

struct Human {
	var name: String
	var gender: Genders
	
	init(name: String, bloodType: String, height: Int) {
			self.name = name
			gender = Genders(bloodType: bloodType, height: height)
		}
}
  • 그리고 optional한 Human 객체를 하나 만들어보자
var miro: Human? = Human(name: "Miro", bloodType: "A", height: 185)
  • 그리고 miro의 height에 접근해보자! 그러면 에러가 난다. 이는 miro가 옵셔널 타입이기 때문이다.
miro.gender.height // error
  • 따라서 아래와 같이 접근을 해야된다. 그리고 만약 miro가 nil이라면 nil이 반환이된다.
miro?.gender.height // 185

🫧 옵셔널 체이닝의 몇가지의 특징(by.소들님)

  • 옵셔널 체이닝의 결과값의 타입은 마지막 표현식의 옵셔널 타입이다.
miro = nil
let height = miro?.gender.height // nil이지만, height의 타입은 Optional<Int>type
  • 옵셔널 체이닝의 마지막 표현식은 옵셔널이더라도 ?를 생략한다!
struct Genders {
	var bloodType: String
	var height: Int?
}

let height = miro?.gender.height? // error
let height = miro?.gender.height // OK
  • 옵셔널 체이닝의 표현식 중 하나라도 nil이라면, 나머지 표현식은 평가하지도 않고 그냥 nil을 return한다.
  • 함수가 껴있는 옵셔널 체이닝의 경우.
    • 함수의 리턴값이 optional인 경우, ()?를 붙혀준다!
struct Human {
	var name: String
	var gender: Genders
	
	init(name: String, bloodType: String, height: Int) {
			self.name = name
			gender = Genders(bloodType: bloodType, height: height)
		}

	func getGenders() -> Genders? {
			return gender
	}
}

miro?.getGenders()?.height // 185
  • 만약 함수 자체가 옵셔널이라면? ?()를 통하여 접근을 한다. ( return 값은 옵셔널 아님!)
struct Human {
	var name: String
	var gender: Genders
	
	init(name: String, bloodType: String, height: Int) {
			self.name = name
			gender = Genders(bloodType: bloodType, height: height)
		}

	func getGenders() -> Genders { // return 값 옵셔널 아님
			return gender
	}
}

let function = miro?.getGenders()

function?().height // OK 
  • 만약 함수 자체도 옵셔널이고, 리턴 값도 옵셔널일 경우에는 ?()? 를 통하여 접근해준다.
struct Human {
	var name: String
	var gender: Genders
	
	init(name: String, bloodType: String, height: Int) {
			self.name = name
			gender = Genders(bloodType: bloodType, height: height)
		}

	func getGenders() -> Genders? {
			return gender
	}
}
let function = miro?.getGenders()

function?()?.height // OK 

📚 참고자료

Swift) Optional 부수기 (3) Optional Unwrapping - 옵셔널 바인딩(if let vs guard let)

Swift) Optional 부수기 (4) Optional Unwrapping - IUO (옵셔널 묵시적 추출)

Swift) Optional 부수기 (6) - Optional Chaining (옵셔널 체이닝)

0개의 댓글