Class(클래스*)*는 class로 정의하고, Structure(구조체)는 struct로 정의

class Dog {
  var name: String?
  var age: Int?

  func simpleDescription() -> String {
    if let name = self.name {
      return "🐶 \\(name)"
    } else {
      return "🐶 No name"
    }
  }
}

struct Coffee {
  var name: String?
  var size: String?

  func simpleDescription() -> String {
    if let name = self.name {
      return "☕️ \\(name)"
    } else {
      return "☕️ No name"
    }
  }
}

var myDog = Dog()
myDog.name = "찡코"
myDog.age = 3
print(myDog.simpleDescription()) // 🐶 찡코

var myCoffee = Coffee()
myCoffee.name = "아메리카노"
myCoffee.size = "Venti"
print(myCoffee.simpleDescription()) // ☕️ 아메리카노

Class(클래스)는 상속 가능

Structure(구조체)는 상속 불가능

class Animal {
  let numberOfLegs = 4
}

class Dog: Animal {
  var name: String?
  var age: Int?
}

var myDog = Dog()
print(myDog.numberOfLegs) // Animal 클래스로부터 상속받은 값 (4)

클래스는 Reference(참조)하고, 구조체는 Copy(복사)

// class
var dog1 = Dog()  // dog1은 **새로 만들어진 Dog()를 참조**합니다.
var dog2 = dog1   // dog2는 **dog1이 참조하는 Dog()를 똑같이 참조**합니다.

dog1.name = "찡코" // dog1의 이름을 바꾸면 Dog()의 이름이 바뀌기 때문에,
print(dog2.name)  // dog2의 이름을 가져와도 **바뀐 이름("찡코")이 출력**됩니다.

// structure
var coffee1 = Coffee()   // coffee1은 새로 만들어진 **Coffee() 그 자체**입니다.
var coffee2 = coffee1    // coffee2는 **coffee1을 복사한 값 자체**입니다.

coffee1.name = "아메리카노" // coffee1의 이름을 바꿔도
coffee2.name             // coffee2는 **완전히 별개이기 때문에 이름이 바뀌지 않습니다. (nil)**

생성자 (Initializer)

클래스와 구조체 모두 생성자를 가지고 있음,

생성자에서는 속성의 초깃값을 지정할 수 있음

class Dog {
  var name: String?
  var age: Int?

  init() {
    self.age = 0
  }
}

struct Coffee {
  var name: String?
  var size: String?

  init() {
    self.size = "Tall"
  }
}

만약 속성이 옵셔널이 아니라면 항상 초깃값을 가져야 함

class Dog {
  var name: String?
  var age: Int // 컴파일 에러!
}

// error: 
// stored property 'age' without initial value prevents synthesized initializers
// 만약 옵셔널이 아닌 속성이 초깃값을 가지고 있지 않으면 컴파일 에러가 발생

속성을 정의할 때 초깃값을 지정해 주는 방법과,