TIL 12일 차 - Swift 필수 문법 문제 4번 풀이

문제 4-1.
- Introducible 프로토콜을 정의하세요.
- name: String 프로퍼티를 요구사항으로 포함합니다.
- introduce() -> String 메서드를 요구사항으로 포함합니다.
- 동작 예시: print("안녕하세요, 저는 \(name)입니다.")
protocol Introducible {
var name: String { get } // name이라는 저장 프로퍼티
func introduce() -> String // 구현은 프로토콜이 아니라 채택한 타입이 직접해야함
}
'
' {get} {get set} 부분 추가 필요
'
문제 4-2.
- Robot, Cat, Dog 타입을 정의하고 Introducible 프로토콜을 채택해 주세요.
- Robot 타입의 경우, name 값이 변경될 때마다 변경 이전값과 이후 값을 출력하도록 구현해 주세요.
- 만약 변경 이전값과 이후값이 같다면 출력하지 않아야 합니다.
- 출력 예시
- name 변경 알림 변경 이전 값: 피겨 변경 이후 값: 옵티머스
내가 처음 작성한 풀이 코드
// 맨 처음 작성한 코드
class Robot: Introducible {
var name: String {
// 프로퍼티 옵져버 쓰기
willSet {
if newValue != self.name {
print("변경 이전 값: \(self.name)")
print("변경 이후 값: \(newValue)")
self.name = newValue
}
}
}
func introduce() -> String {
return "안녕하세요, 저는 \(name) 입니다."
}
func batteryCharge() -> String {
return "전원을 충전합니다."
}
init(name: String) {
self.name = name
}
}
// Cat과 Dog 클래스는 Robot 클래스의 WillSet 부분을 제외하고 유사하므로 생략...
여기에서 핵심 포인트인 name 변수 + WillSet에 대해 알아보자
이번 과제를 통해 처음 배웠던 이 willSet { }은 이 값이 바뀌기 직전에 Swift가 자동으로 실행해 주는 감시 코드이다.
프로퍼티 옵서버{Property Observer)에는 두 가지 종류가 있는데
willSet(바뀌기 직전) didSet(바뀐 직후)가 있다.
내가 사용한 name에 붙은 willSet은 name에 새 값이 들어가기 전에 먼저 실행되어 name이 변경되기 직전에 WillSet을 실행해 newValue(바뀔 값)과 name(현재 값)이 같은지 확인한다. 그다음에 값을 바꾸게 된다.
같지 않을 때엔 내가 만든 문자열을 출력해 주게 된다.
만약 name에 새 값을 대입하려고 하면 스위프트는 그 새로운 값을 자동으로 newValue라는 이름으로 예약값을 제공한다.
그리고 willSet에 들어있는 name은 바뀌기 전에 원래 점유하고 있던 기존 값을 뜻한다.
다르다면 로그를 출력하고 Swift가 자동으로 새 값을 대입해 주므로 name = newValue와 같은 코드를 써줄 필요가 없다.
그리고 self.name 이 곧 name이므로 굳이 self. 을 붙여줄 필요는 없어 삭제하였다.
수정한 코드
// 수정한 코드
class Robot: Introducible {
var name: String {
// 프로퍼티 옵져버 쓰기
willSet {
if newValue != name {
print("변경 이전 값: \(name)")
print("변경 이후 값: \(newValue)")
}
}
}
func introduce() -> String {
return "안녕하세요, 저는 \(name) 입니다."
}
func batteryCharge() -> String {
return "전원을 충전합니다."
}
init(name: String) {
self.name = name
}
}
// Cat과 Dog 클래스는 Robot 클래스의 WillSet 부분을 제외하고 유사하므로 생략...
var robot = Robot(name: "로봇")
robot.name = "로봇" // willSet 출력문 실행 X
robot.name = "로봇 이름 변경" // name 변경 알림 변경 이전 값: 로봇 변경 이후 값: 로봇 이름 변경 출력
그럼 이 willSet을 대체 쓰는 여러 가지 경우들이 너무 궁금했다. 해당 부분은 프로퍼티 옵서버를 정리해 추후에 블로그 글을 올려보려 한다.
문제 4-2.
- [Introducible] 타입 배열을 정의하고, Robot, Cat, Dog 인스턴스 1개씩을 append 해주세요.
- 배열을 순회하며 각 타입 고유의 메서드들을 호출하는 코드를 작성해 주세요.
var cat = Cat(name: "냥이")
var dog = Dog(name: "멈무")
var arrayIntroducible = [Introducible]()
arrayIntroducible.append(robot)
arrayIntroducible.append(cat)
arrayIntroducible.append(dog)
for i in arrayIntroducible { // arrayIntroducible 배열은 원소가 Introducible 타입임
// i.batteryCharge() error: Value of type 'any Introducible' has no member 'batteryCharge'
// Introducible 타입에는 각 인스턴스들의 메소드가 당연히 없음
if let robot = i as? Robot {
print(robot.batteryCharge()) // 전원을 충전합니다. 출력
}
if let cat = i as? Cat {
print(cat.grooming()) // 야옹 출력
}
if let dog = i as? Dog {
print(dog.bark()) // 멍멍멍 출력
}
}
일단 i.batterycharge문이 오류가 나는 이유는 Introducible에는 name, introduce()만 있기 때문에 오류가 발생한다.
해당하는 객체가 진짜 Robot이라면 Robot처럼 써도 되게 하는 방법이 바로 타입 캐스팅이며
그중 가장 안전한 방식은 as?이다.
if let robot = i as? Robot { print(robot.batteryCharge) } 문을 사람 말로 번역하자면
만약 i의 진짜 정체가 Robot이면 Robot으로 변신시켜서 robot에 담고 print 문을 실행하라.
실패하면 nil을 반환해라.. 그래서 옵셔널 형태인 것이다.
만약 as! 를 사용한다면 이건 좀 위험한 방식이라고 할 수 있다. 만약 Robot이 아니라면 즉시 크래시가 발생하게 된다.
그래서 as? + if let을 사용하는 것이 안전하고 정석적인 방식이라고 할 수 있다.
여기서 마무리하려다 또 궁금한 게 생겼다.
근데 arrayIntroducible이 [Introducible] 형태여야 하는데 원소인 robot을 Robot형태로 바꿔도 되나???라는 질문이 생겼는데
- Robot은 Introducible을 채택
- 그래서 Robot은 Introducible로 취급 가능 (업캐스팅)
여기까지는 이해되었다.
❓ 내 질문
[Introducible] 배열인데
원소를 Robot으로 바꿔도 되나?
✅ 답
안 바꿨다.
Robot으로 “잠깐 바라봤을 뿐”이다.8️⃣ 한 문장으로 최종 정리 (이거 외워도 됨)
[Introducible] 배열에서 as? Robot을 쓰는 것은
배열의 타입이나 원소를 바꾸는 게 아니라,
같은 객체를 더 구체적인 타입의 변수로 임시 참조하는 것이다.
'iOS > Swift ' 카테고리의 다른 글
| [iOS-Swift] 클로저 closure 심화 (0) | 2026.01.12 |
|---|---|
| [iOS- Swift] 스위프트 필수 문법 문제 5번 풀이 (0) | 2026.01.10 |
| [iOS- Swift] 스위프트 필수 문법 문제 3번 풀이 (0) | 2026.01.09 |
| [iOS- Swift] 스위프트 필수 문법 문제 2번 풀이 (0) | 2026.01.09 |
| [iOS-Swift] 스위프트 필수 문법 문제 1번 풀이 (0) | 2026.01.09 |