var name = "Alice"
var age = 20
let birthYear = 2003
var car: String? = "BMW"
print("이름: \(name), 나이: \(age), 출생 연도: \(birthYear), 보유 차종: \(car)")
→ 이름: Alice, 나이: 20, 출생 연도: 2003, 보유 차종: Optional("BMW") 출력
car = nil
print("이름: \(name), 나이: \(age), 출생 연도: \(birthYear), 보유 차종: \(car)")
→ 이름: Alice, 나이: 20, 출생 연도: 2003, 보유 차종: nil 출력
print(type(of: car))
→ Optional<String> 출력
변환 결과 확인
변수 score가 옵셔널 타입(Int?)인 이유
: 주어진 name 값이 딕셔너리에 존재하지 않는 키일 가능성이 있으므로 그 경우에는 딕셔너리가 nil을 반환하기 때문에
방법 2 - 방법 1 순으로 순서 변경 시 출력 메시지 비교
: "Jane: 점수 없음. early return" 출력 후 return되어 코드 종료
: 방법 1 먼저 실행 시, "Jane: 점수 없음. else 문 실행" 출력 후 return되어 코드 종료
방법 3(강제 언래핑) 가장 먼저 실행 시
: "Jane의 점수 조회: \n "정수: nil점" 출력 후 Fatal Error와 함께 코드 강제 종료 - nil값을 강제 언래핑 시도했으므로
"그만"을 입력할 때까지 반복적으로 게임을 진행한다.enum Choice: String, CaseIterable {
case 가위 = "가위", 바위 = "바위", 보 = "보"
func result(against: Choice) -> String {
if self == against {
return "나: \(self.rawValue), 컴퓨터: \(against.rawValue) \n무승부입니다!"
}
switch (self, against) {
case (.가위, .바위), (.바위, .보), (.보, .가위):
return "나: \(self.rawValue), 컴퓨터: \(against.rawValue) \n졌습니다..."
default:
return "나: \(self.rawValue), 컴퓨터: \(against.rawValue) \n이겼습니다!"
}
}
}
→ Choice 열거형에 result 메소드를 생성하여 열거형에서 바로 결과를 알 수 있도록 함
func getComChoice() -> Choice {
return Choice.allCases.randomElement()!
}
→ .allCases 메소드 활용을 위해 열거형에 CaseIterable 프로토콜 채택
func rockPaperScissors(_ myChoice: String) {
if myChoice == "그만" {
print("가위뱌위보를 종료합니다.")
isPlaying = false
return
}
let com = getComChoice()
let my = Choice(rawValue: myChoice)
guard let my = my else {
print("올바른 값을 입력해주세요.(가위, 바위, 보)")
return
}
print(my.result(against: com))
}
→ myChoice가 "그만"인지 우선 판별 - 맞을 경우 함수 종료
→ 아닐 경우 랜덤값 com 생성, 입력받은 문자열로 Choice 타입의 my 생성
→ my가 nil이 아닐 경우 my.result(against:)로 결과 반환
→ my가 nil일 경우 재입력 요청
var isPlaying = true
while isPlaying {
print("가위, 바위, 보 중 하나를 입력해주세요.")
rockPaperScissors(readLine() ?? "")
}
→ isPlaying 변수로 while문의 반복 여부 확인
→ 입력값 "그만"일 경우 isPlqying = false, 반복문 종료
➡︎ 처음에는 가위, 바위, 보 문자열이 아니라 각 값을 정수로 치환하여 승패를 판단하려했는데, 튜터님의 힌트로 열거형을 사용하게 되었다.
열거형에서 메소드 선언은 처음해보았는데, 다양한 상황에서 활용할 수 있을 문법같다..
1) Struct 구현하기
struct Product {
var id: UUID = UUID()
var name: String
var price: Int
var category: String
}
var a = Product(name: "치약", price: 2000, category: "생필품")
var b = Product(name: "비누", price: 1000, category: "생필품")
a.price = 3000
print("a 가격: \(a.price), b 가격: \(b.price)")
// a 가격: 3000, b 가격: 1000 출력
→ 한 인스턴스의 변경이 다른 인스턴스에 영향 없음
2) Class 사용 예제)
class Product {
var id: UUID = UUID()
var name: String
var price: Int
var category: String
init(name: String, price: Int, category: String) {
self.name = name
self.price = price
self.category = category)
}
}
var a = Product(name: "치약", price: 2000, category: "생필품")
var b = Product(name: "비누", price: 1000, category: "생필품")
a.price = 3000
print("a 가격: \(a.price), b 가격: \(b.price)")
// a 가격: 3000, b 가격: 1000 출력
→ 서로 다른 두 인스턴스를 참조할 경우, 한 참조의 값을 변경하여도 다른 인스턴스에 영향 없음
class ShoppingCart {
var items: [Product]
var totalPrice: Double {
return items.reduce(0) { $0 + $1.price }
}
init(items: [Product]) {
self.items = items
}
func addItem(_ item: Product) {
items.append(item)
}
func removeItem(at idx: Int) {
items.remove(at: idx)
}
}
var cart = ShoppingCart(items: [a, b])
var anotherCart = cart
print(cart.items)
// "치약", "비누" 출력
anotherCart.addItem(Product(name: "샴푸", price: 7000, category: "생필품"))
print(cart.items)
// "치약", "비누", "샴푸" 출력
→ 동일한 하나의 인스턴스를 서로 다른 2개의 변수에서 참조할 경우, 한 참조(변수)에서 값을 변경한 경우 다른 참조(변수)에도 반영됨
Discountable 프로토콜
protocol Discountable {
var originalPrice: Double { get }
func discountedPrice(rate: Double)
}
struct PercentageDiscount: Discountable {
var originalPrice: Double
func discountedPrice(rate: Double) {
let discounted = originalPrice * (1 - rate)
print("\(rate * 100)% 할인된 가격은 \(discounted)원입니다.")
}
}
var c = PercentageDiscount(originalPrice: 10000)
c.discountedPrice(rate: 0.2)
// 20.0% 할인된 가격은 8000.0원입니다. 출력
→ Discountable 프로토콜을 채택, 필수 프로퍼티와 메소드 구현
class VIPDiscount: Discountable {
enum Grade: String {
case gold = "Gold", silver = "Silver", bronze = "Bronze"
}
var grade: Grade
var originalPrice: Double
var rate: Double
init(grade: Grade, originalPrice: Double) {
self.grade = grade
self.originalPrice = originalPrice
switch grade {
case .gold: self.rate = 0.15
case .silver: self.rate = 0.1
case .bronze: self.rate = 0.05
}
}
func discountedPrice(rate: Double) {
let discounted = originalPrice * (1 - rate)
print("\(self.grade.rawValue) 등급의 할인된 가격은 \(discounted)원입니다.")
}
}
var d = VIPDiscount(grade: .gold, originalPrice: 10000)
d.discountedPrice(rate: d.discount)
// Gold 등급의 할인된 가격은 8500.0원입니다. 출력
→ Discountable 프로토콜을 채택, 필수 프로퍼티와 메소드 구현
4. 프로토콜 확장(Extension)
protocol Discountable {
var originalPrice: Double
func discountedPrice(rate: Double) -> Double
}
extension Discountable {
func finalPrice() -> Double {
return discountedPrice(rate: 0.1) // 기본 10% 할인
}
}
→ 확장에는 저장 속성 구현 불가, 메소드 리턴값 명시 필요
→ 확장에서 메소드 사용 시 아규먼트 기재 필요