클래스는 class , 구조체는 struct 로 정의합니다.
class Cat {
var name: String?
var age: Int?
func simpleDescription() {
if let name = self.name {
print("😺 \(name)")
} else {
print("😺 No Name")
}
}
}
struct Coffee {
var name: String?
var size: String?
func simpleDescription() {
if let name = self.name {
print("☕️\(name)")
} else {
print("☕️ No Name")
}
}
}
var myCat = Cat()
myCat.name = "숑이"
myCat.age = 7
myCat.simpleDescription() // 😺 숑이
var myCoffee = Coffee()
myCoffee.name = "아메리카노"
myCoffee.size = "Venti"
myCoffee.simpleDescription() // ☕️아메리카노
클래스는 상속이 가능합니다. 구조체는 불가능합니다.
class Animal {
var name: String?
var age: Int?
var numberOfLegs = 4
func simpleDescription() {
if let name = self.name {
print("animal : \(name)")
} else {
print("animal : No Name")
}
}
}
class Cat: Animal {
override func simpleDescription() {
if let name = self.name {
print("😺 \(name)")
} else {
print("😺 No Name")
}
}
}
myCat.simpleDescription() // 😺 숑이 (오버라이드한 함수 호출)
print(myCat.numberOfLegs) // Animal 클래스로부터 상속 받은 값 4
클래스는 참조(Reference)하고 구조체는 복사(Copy)합니다.
var cat1 = Cat() // cat1은 새로 만들어진 Cat()을 참조
var cat2 = cat1 // cat2는 cat1이 참조하는 Cat()을 똑같이 참조
cat1.name = "숑이"
cat1.age = 7
// cat1 의 멤버 변수를 변경하면, cat1을 참조하고 있는 cat2의 내용을 출력했을 때, cat1과 동일하게 나옵니다.
cat1.simpleDescription() // 😺 숑이
cat2.simpleDescription() // 😺 숑이
var coffee1 = Coffee() // cofffee1은 새로 만들어진 Coffee() 그 자체
var coffee2 = coffee1 // coffee2는 coffee1을 복사한 값 그 자체
coffee1.name = "아메리카노"
coffee1.size = "Venti"
// 따라서 coffee1의 내용을 변경해도 coffee2는 변하지 않습니다.
coffee1.simpleDescription() // ☕️아메리카노
coffee2.simpleDescription() // ☕️ No Name
클래스와 구조체는 모두 생성자를 가지고 있습니다. 생성자에서 멤버 변수를 초깃값을 지정할 수 있습니다.
class Animal {
var name: String?
var age: Int?
var numberOfLegs = 4
init() {
self.name = "아무개"
self.age = 1
}
}
struct Coffee {
var name: String?
var size: String?
init() {
name = "커피"
size = "Tall"
}
}
만약 멤버 변수가 옵셔널이 아니라면, 항상 초깃값을 가져야 합니다. 그렇지 않으면, 컴파일 에러가 발생합니다.
class Animal {
var name: String?
var age: Int // error
var numberOfLegs = 4
init() {
// self.age = 1 <- 이렇게 초기화 해줘야 에러 발생하지 않음
}
}
위 처럼 init() 안에서 초기화를 해주거나 아래와 같이 변수를 선언과 동시에 초기화 해야 한다.
class Animal {
var name: String?
var age: Int = 1
var numberOfLegs = 4
}
생성자도 함수와 마찬가지로 파라미터를 받을 수 있습니다.
class Animal {
var name: String?
var age: Int
var numberOfLegs = 4
init(name: String, age: Int) {
self.name = name
self.age = age
}
func simpleDescription() {
if let name = self.name {
print("animal : \(name)")
} else {
print("animal : No Name")
}
}
}
class Cat: Animal {
override func simpleDescription() {
if let name = self.name {
print("😺 \(name)")
} else {
print("😺 No Name")
}
}
}
var myCat = Cat(name: "숑이",age: 7)
myCat.simpleDescription() // 😺 숑이
만약 클래스를 상속받은 클래스에서 생성자를 구현하려면, 상위 클래스의 생성자를 호출해야 합니다. 만약 상위 클래스의 생성자와 파라미터가 모두 같다면, override 키워드를 붙여야합니다.
super.init() 은 클래스의 변수가 모두 초기화 된 후 호출되어야 합니다.
그리고나서 자기 자신에 대한 self 키워드를 사용할 수 있습니다.
class Cat: Animal {
var hairColor: String?
override init(name: String, age: Int) {
self.hairColor = "gray"
super.init(name: name, age: age)
self.simpleDescription()
}
override func simpleDescription() {
if let name = self.name {
print("😺 \(name)")
} else {
print("😺 No Name")
}
}
}
만약 위 예시 코드에서 super.init() 이전에 self.simpleDescription()을 호출하면 컴파일 에러가 발생합니다.
override init(name: String, age: Int) {
self.hairColor = "gray"
self.simpleDescription() // error
super.init(name: name, age: age)
}
'self' used in method call 'simpleDescription' before 'super.init' call
self.simpleDescription()
deinit 은 메모리에서 해제된 직후에 호출됩니다.
class Cat: Animal {
...
deinit {
print("메모리 해제 됨")
}
...
}
속성은 크게 두가지로 나뉩니다. 값을 가지는 속성 (Stored Property)과 계산되는 속성 (Computed Property) 인데요. Stored Property 는 값 자체를 가지고 있는 속성이고, Computed Property 는 어떠한 계산을 통해 반환되어지는 값 입니다.
우리가 지금까지 정의하고 사용한 name, age 와 같은 속성들은 모두 Stored Property 입니다. Computed Property 는 get, set 을 사용해서 정의할 수 있습니다. set 에서는 새로 설정된 값을 newValue 라는 예약어를 통해 접근할 수 있습니다.
struct Hex {
var decimal: Int?
var hexString: String? {
get {
if let decimal = self.decimal {
return String(decimal, radix: 16)
} else {
return nil
}
}
set {
if let newValue = newValue {
self.decimal = Int(newValue, radix: 16)
} else {
self.decimal = nil
}
}
}
}
var hex = Hex()
hex.decimal = 10
print(hex.hexString) // "a"
hex.hexString = "b"
print(hex.decimal) // 11
위 코드에서 hexString 은 실제로 값을 가지고 있지 않지만, decimal 로부터 값을 가져와 16진수 문자열로 만들어서 반환합니다.
decimal 은 Stored Property, hexString 은 Computed Property 입니다.
추가적으로, get 만 정의할 경우, get 키워드를 생략 가능합니다. 이런 속성을 Read Only(읽기 전용) 라고 합니다.
struct Hex {
...
var hexString: String? {
if let decimal = self.decimal {
return String(decimal, radix: 16)
} else {
return nil
}
}
}
willSet, didSet 을 사용하면, 속성에 값이 지정되기 직전, 직후에 원하는 작업을 할 수 있습니다.
struct Hex {
var decimal: Int? {
willSet {
print("\(self.decimal)에서 \(newValue)로 값이 변경될 예정입니다")
}
didSet {
print("\(oldValue)에서 \(self.decimal)로 값이 변경 되었습니다.")
}
}
}
willSet은 set 과 마찬가지로 새로운 값을 newValue로 가져올 수 있고, didSet 은 바뀌기 이전 값에 oldValue 로 접근할 수 있습니다.