swift에서 struct와 class의 차이점에 대해 정리한다.
가장 큰 차이점은 superclass로부터 상속받을 수 있다는 점이 있지만,
그것과는 별개로 superclass로부터 상속받지 않는 기본 class와 struct의 차이점을 정리할 것이다.
// ##### Enemy.swift #####
struct Enemy {
// ##### 1 ######
var health : Int
var attackStrength : Int
// structs are immutable --> mutating func 을 사용해야 property의 값을 바꿀 수 있다.
mutating func takeDamage(amount: Int) {
health -= amount
}
func move() {
print("Move forward")
}
}
// ##### main.swift #####
// ##### 2 #####
var skeleton1 = Enemy(health: 100, attackStrength: 10)
var skeleton2 = skeleton1
// struct의 경우, skeleton1에서 초기화시킨 것을 그대로 복사한다.
// skeleton1과 skeleton2는 서로 다르다.
skeleton1.takeDamage(amount: 10)
print(skeleton1.health)
// 90
print(skeleton2.health)
// 100, skeleton1과 skeleton2는 별개이기 때문이다.
skeleton1.takeDamage(amount: 10)
skeleton2.takeDamage(amount: 10)
print(skeleton1.health)
// 80
print(skeleton2.health)
// 90
1. Struct에서는 자동으로 initializer가 제공(?)되기 때문에 따로 init method를 작성해줄 필요가 없다.
2. Struct는 값 유형(value type semantic)이기 때문에 '값'을 전달한다.
그리고 값을 바꾸면 바뀐 상태로 복사되어서 다시 할당된다.
예를 들어,
어떤 사진을 전달한다고 가정하면, 그 사진을 복사해서 전달한다.
여러 명이 사진을 전달받았을 경우
누군가가 전달받은 사진에 낙서를 한다고 해도, 원본이나 다른 사람들에게 전달한 사진에 영향을 주지 않는다.
따라서 let으로 초기화시킬 경우 struct의 값을 변경하고자 할 때 경고메시지가 뜨면서 막히게 된다.
skeleton1, skeleton2를 var로 초기화시켜야 takeDamage() method를 사용하여 health 값을 변경할 수 있게 되는 것이다.
// ##### Enemy.swift #####
class Enemy {
var health : Int
var attackStrength : Int
//####### 1 #######
init(health: Int, attackStrength: Int) {
self.health = health
//self.health : Enemy's property
//health : got passed in as the input
self.attackStrength = attackStrength
}
func takeDamage(amount: Int) {
health -= amount
}
func move() {
print("Move forward")
}
}
// ##### main.swift #####
// ########## 2 ########
let skeleton1 = Enemy(health: 100, attackStrength: 10)
let skeleton2 = skeleton1
// skeleton1에서 초기화시킨 object를 참조했다. 즉, 동일한 object에 대해 2개의 레퍼런스가 생긴 것이다.
// let skeleton2 = Enemy(health: 100, attackStrength: 10) 을 입력했다면
// skeleton1에서 초기화한 것과 동일한 object를 초기화했을 것이다.
skeleton1.takeDamage(amount: 10)
print(skeleton1.health)
// 90
print(skeleton2.health)
// 90 : class가 참조로 전달되기 때문, skeleton2는 skeleton1에서 참조한 것을 똑같이 참조했다.
skeleton2.takeDamage(amount: 10)
print(skeleton1.health)
// 80
print(skeleton2.health)
// 80
1. class에는 init method를 별도로 작성해줘야 한다.
2. class는 참조 유형(reference type semantic)이기 때문에 '레퍼런스'에 의해 전달된다.
예를 들어,
사진을 전달한다면, 사진이 있는 위치 - /Users/Desktop/photo.jpg -를 전달한다.
여러 명이 사진의 레퍼런스를 전달받았을 경우,
누군가가 전달받은 사진에 낙서를 하거나, 삭제를 할 경우 해당 레퍼런스를 사용하는 모든 사람에게 영향을 준다.
모두가 낙서가 된 사진을 갖게 되거나, 사진이 삭제되는 것이다.
이러한 문제점이 있기 때문에 Apple에서는 struct를 사용하는 것을 디폴트로 할 것을 권장한다.
- 특별히 상속이 필요하거나, Objective-C 코드를 가지고 작업해야 할 경우 class로 바꿔서 사용할 것을 권장한다.
레퍼런스가 동일하게 유지되는 동안에는
값을 변경할 때마다 메모리의 다른 곳에 저장된 object가 변경될 수 있다고 한다.
무슨 말인지는 잘 모르겠지만..
어쨋거나 결론은 Class에서는
참조하는 변수(skeleton1,2)가 변경 가능한지의 여부와 관계없이 class's member를 수정할 수 있다.
따라서 skeleton1, 2를 let으로 초기화시키더라도 class의 값을 수정할 수 있다는 점이 struct에서와의 차이점이다.
이 때 주의해야 할 점은 Enemy class 내에 있는 health나 attackStrength 등이 let으로 입력되어 있었다면,
그 값은 변경할 수 없다.
'iOS > swift' 카테고리의 다른 글
swift - Optional (basic) (0) | 2021.08.25 |
---|---|
swift - Split vs Components (문자열을 특정 separator로 나눠서 배열에 리턴) (0) | 2021.08.12 |
Swift - Class & Inheritance (0) | 2021.07.19 |
swift - immutability (구조체 안에서) (0) | 2021.07.01 |
swift - Functions with Output (0) | 2021.06.29 |