[Swift] 옵셔널 타입의 응용

임승섭·2023년 6월 28일
0

Swift

목록 보기
20/35

옵셔널 체이닝 Optional Chaining

  • 옵셔널 타입에 대해 접근연산자를 호출하는 방법
  • 옵셔널 체이닝의 결과는 항상 옵셔널 (if let 바인딩이나, 강제 언래핑으로 벗겨줘야 함)
  • 옵셔널 체이닝의 값 중에 하나라도 nil이면, 이어지는 표현식 평가하지 않고 nil 리턴
class Dog {
    var name: String?	// 이름은 optional. 초기값 없으면 nil
    var weight: Int
    
    init(name: String, weight: Int) {
        self.name = name
        self.weight = weight
    }
    
    func sit() {
        print("\(self.name)가 앉았습니다.")
    }
    
    func layDown() {
        print("누웠습니다.")
    }
}

class Human {
    var dog: Dog?		// 있으면 Dog, 없으면 nil
}

// 변수 생성 (Dog타입)
var choco = Dog(name: "초코", weight: 15)
choco.name		// Optional("초코")
choco.sit()		// Optional("초코")가 앉았습니다.

/*
// 변수 생성 (Optional Dog 타입) - 옵셔널 체이닝
var choco: Dog? = Dog(name: "초코", weight: 15)
// 앞에 변수가 옵셔널의 가능성이 있을 때, 물음표를 붙여준다 -> "옵셔널 체이닝"
choco?.name			// Optional("초코")
choco?.sit()		// Optional("초코")가 앉았습니다.
choco?.name = "초코잉"		// 이름 바꾸려고 할 때도 항상 물음표를 붙여줘야 한다
*/


var human = Human()
human.dog = choco		// human의 dog는 Dog? 타입
human.dog?.name			// Optinoal("초코"). 맨 뒤 name은 ?를 안붙여줘도 된다.

/*
// 변수 생성 (Optional Human 타입)
var human2: Human? = Human()
human2?.dog = choco		// 할당할 때도 물음표 붙여주기
human2?.dog?.name		// human도 optional, dog도 optional
*/

// name도 optional이긴 하지만, 마지막에 쓸 때는 물음표를 붙여주지 않는다


// 실제 사용시에는 결국 Unwrapping해줘야 한다
// 1) 앞의 옵셔널타입에 값이 있다는 것이 확실한 경우

print(human2!.dog!.name)          // name 자체가 옵셔널타입이기 때문에 Optional("초코얌")
print(human2!.dog!.name!)
print(human2!.dog!.weight)        // weight 자체는 옵셔널타입이 아니기 때문에   15



// 2) if let 바인딩
if let name = human2?.dog?.name {    // Optional("초코얌")
    print(name)                      // 초코얌
}



// 3) Nil-Coalescing 연산자
var defaultName = human2?.dog?.name ?? "멍탱구리"
print(defaultName)

활용 예시

옵셔널 체이닝 - 함수 관련

class Cat {
	var name: String?							// 옵셔널 문자열
    var myMaster: ( () -> Person? )?			// 변수 타입이 옵셔널 클로저(함수)
    
    // 생성자 - 함수가 생성자의 흐름 범위를 벗어나 - escaping 키워드 필요
    init (aFunction: @escaping () -> Person?) {
    	self.myMaster = aFunction
    }
}

class Person {
	var name: String?		// 옵셔널 String
}


// 함수 정의
func meowmeow() -> Person? {	// 인풋 void, 아웃풋 optional Person
	let person = Person()
    person.name = "Jobs"
    return person				// 옵셔널 Person 타입으로 리턴
}

// 옵셔널 Cat 타입의 변수 생성
var cat: Cat? = Cat(aFunction: meowmeow)


var name = cat?.myMaster?()?.name	// Optional("Jobs")

/*
cat? -> cat에 접근
cat?.myMaster() -> 함수 실행 -> x
cat?.myMaster?()? -> 소괄호 앞뒤로 물음표 -> o
	앞에 있는 ? : 함수가 없을 수도 있다는 뜻. (함수 타입이 optional이기 때문)
	뒤에 있는 ? : 함수의 결과값, Person이 없을 수도 있다는 뜻. (리턴 타입이 Optional Person)
cat?.myMaster?()?.name	-> name에 접근 Optional("Jobs")
*/

// if let 바인딩으로 마무리
if let name = cat?.myMaster?()?.name {
	print(name)
}

옵셔널 체이닝 - 딕셔너리 관련

class Library1 {
	var books: [String: Person]?	// 옵셔널 딕셔너리 -> 있을수도 있고 없을수도 있다.
}

var person1 = Person()
person1.name = "Jobs"
print(person1.name)		// Optional("Jobs")

var person2 = Person()
person2.name = "Musk"

var library = Library1()
library.books = ["Apple": person1, "Tesla": person2]

/*
library.books["Apple"].name -> x
library.books?["Apple"]?.name -> 앞뒤로 물음표 -> o
	앞에 있는 물음표 : 딕셔너리가 없을 수 있다 (타입이 옵셔널 딕셔너리)
    뒤에 있는 물음표 : 딕셔너리의 값이 없을 수 있다 (결과값이 없을 수도 있다)
*/

// if let 바인딩으로 마무리
if let name = library.books?["Apple"]?.name {
	print("이름 : ", name)
}

옵셔널 체이닝 - 함수의 실행

  • 옵셔널 타입에 접근해서 사용하는 함수는 앞의 타입을 벗기지 않아도 사용가능하다
    함수 자체가 옵셔널은 아니기 때문에 함수를 벗길 필요가 없다
  • 함수가 리턴형이 없는 경우
    1. 타입에 값이 있으면 함수 실행
    2. 타입에 값이 없으면 nil
  • 함수가 리턴형이 있는 경우
    1. 타입에 값이 있으면 옵셔널 리턴 타입으로 반환 (원래 리턴형이 옵셔널이 아니더라도)
    2. 타입에 값이 없으면 nil
var bori: Dog? = Dog(name: "보리", weight: 20)
bori.sit() -> x
bori?.sit()	-> o	// Optional("보리")가 앉았습니다

// 메서드의 실행은 굳이 이렇게까지 벗겨서 할 필요가 없어
if let b = bori {
	b.sit()
    b.layDown()
}

// bori가 nil이면 애초에 실행도 안하고 nil을 리턴하기 때문
bori = nil
bori?.layDown		// 함수 실행되지 않고 nil 반환

0개의 댓글