1. 구조체
구조체는 struct 키워드로 정의합니다. 구조체를 정의한다는 것은 새로운 타입을 생성해주는 것과 마찬가지이기 때문에 기본 타입 이름처럼 대문자 카멜케이스를 사용해 이름을 짓습니다. 구조체는 상속이 되지 않습니다.
struct [구조체 이름] {
[프로퍼티와 메소드]
}
struct Person {
// "이름" 부분은 초기값을 정해주는 부분입니다.
// 이렇게 초기값을 정해주면 인스턴스를 생성할 때 값을 생략할 수 있습니다.
var name: String = "이름"
var age: Int
var height: Int
var weight: Int
}
// 구조체의 인스턴스 생성 및 초기화
var axce: Person = Person(name: "Sudon Noh", age: 31, height: 176, weight: 80)
print(axce.name) // Sudon Noh
print(axce.age) // 31
2. 클래스
클래스는 class 키워드로 정의합니다. 클래스도 구조체와 마찬가지로 새로운 타입을 생성해주는 것과 같기 때문에 대문자 카멜케이스를 사용합니다.
class [클래스 이름] {
[프로퍼티와 메소드]
}
클래스를 상속 받을 때는 클래스 이름 뒤에 콜론(:)을 붙이고 부모 클래스의 이름을 명시합니다.
class [클래스 이름]: [부모 클래스 이름] {
[프로퍼티와 메소드]
}
class KoreaMember {
var team: String = ""
var number: Int = 0
var position: String = ""
}
// 클래스의 인스턴스 생성 및 초기화
var lee: KoreaMember = KoreaMember()
lee.team = "마요르카"
lee.number = 19
lee.position = "공격형 미드필더"
"""
구조체에서는 상수 let으로 선언하면 인스턴스 내부의 프로퍼티 값을 변경할 수 없습니다.
하지만 클래스에서의 인스턴스는 참조 타입이기 때문에 클래스의 인스턴스를 상수 let으로
선언하더라도 내부 프로퍼티 값을 변경할 수 있습니다.
"""
let son: KoreaMember = KoreaMember()
son.team = "토트넘"
son.number = 7
son.position = "공격수"
클래스의 소멸
클래스의 인스턴스는 참조 타입이기 때문에 더는 참조할 필요가 없을 때 메모리에서 해제됩니다. 이 과정을 소멸이라고 하는데 소멸되기 직전에 deinit(deinitializer) 이라는 메소드가 호출됩니다.
class EnglishMember {
var name: String = ""
var team: String = ""
var number: Int = 0
var position: String = ""
deinit {
print("\(name) 선수가 영국 대표팀에서 제외되었습니다.")
}
}
var kane: EnglishMember? = EnglishMember()
kane?.name = "Kane"
kane?.team = "토트넘"
kane?.number = 10
kane?.position = "공격수"
// 이때까지는 대표팀에 소속되어 있다.
// deinit 을 실행하고 소멸된다.
kane = nil
// Kane 선수가 영국 대표팀에서 제외되었습니다.
"""
deinit 메소드에는 인스턴스가 메모리에서 해제되기 직전에 처리할 코드를 넣어줍니다.
예를 들어 인스턴스 소멸 전에 테이터를 저장한다거나 다른 객체에 인스턴스 소멸을 알려주어야
할 때 구현합니다.
"""
3. 구조체와 클래스의 차이(값 복사와 참조)
구조체는 값 타입이고 클래스는 참조 타입입니다. 예를 들어 어떤 함수의 전달인자로 값 타입의 값을 넘긴다면 전달될 값이 복사되어 전달됩니다. 하지만 참조 타입이 전달인자로 전달될 때는 값을 복사하지 않고 참조 주소가 전달됩니다.
아래 코드를 예시로 보시면 더 쉽게 이해하실 수 있습니다.
// 구조체 예시
struct PlayerInfo {
let name: String
var age: Int
var team: String
}
var player1: PlayerInfo = PlayerInfo(name: "Sudon Noh", age: 31, team: "Hanhwa Eagles")
player1.age = 100
print("player1's age:", player1.age)
// player1's age: 100
// player2에 player1을 할당합니다.
var player2: PlayerInfo = player1
// player2에 player1에서 지정했던 100의 값이 그대로 복사되어 들어갑니다.
print("player2's age:", player2.age)
//player2's age: 100
// player2의 age값을 999로 변경해줍니다.
player2.age = 999
print("player1's age:", player1.age)
print("player2's age:", player2.age)
// player1's age: 100
// player2's age: 999
// player2 는 player1 의 값 자체를 복사해서 왔기 때문에 변화가 없습니다.
// 클래스 예시
class PlayerInfo2 {
var name: String = ""
var age: Int = 0
var team: String = ""
}
var player3: PlayerInfo2 = PlayerInfo2()
player3.name = "Sudon Noh"
player3.age = 31
player3.team = "Hanhwa Eagles"
// player3의 값을 player4로 할당해줍니다.
var player4: PlayerInfo2 = player3
print("player3's age:", player3.age)
print("player4's age:", player4.age)
// player3's age: 31
// player4's age: 31
"""
player4의 age값을 100으로 변경해줍니다.
player3의 age값은 31이었고, player4의 age값은 100으로 변경했기 때문에
player3의 age값이 31일 것으로 기대됩니다.
하지만 print 해보면 player3의 age값이 100인 것을 확인할 수 있습니다.
"""
player4.age = 100
print("player3's age:", player3.age)
print("player4's age:", player4.age)
// player3's age: 100
// player4's age: 100
// player3과 player4가 참조하는 곳이 같기 때문에 값을 복사했을 때
// 값이 같이 변경됩니다.
아래에서는 메소드는 구조체와 클래스를 각각 받아 age를 변경하는 메소드들 입니다. 메소드에서 사용할 경우 어떻게 값이 변화하는지 확인해보도록 하겠습니다.
// 구조체 age 변경 함수
func changeInfo(_ info: PlayerInfo) {
var copiedInfo: PlayerInfo = info
copiedInfo.age = 1
}
// 클래스 age 변경 함수
func changeInfo2(_ info: PlayerInfo2) {
var copiedInfo: PlayerInfo2 = info
copiedInfo.age = 9999
}
changeInfo(player1)
print("changeInfo player1's age: \(player1.age)")
// changeInfo로 전달된 것은 player1의 값이기 때문에
// 함수를 통해 player1의 age를 1로 바꾸더라도 player1의 값은 변경되지 않습니다.
changeInfo2(player3)
print("changeInfo2 player3's age: \(player3.age)")
print("changeInfo2 player4's age: \(player4.age)")
// changeInfo2 player3's age: 9999
// changeInfo2 player4's age: 9999
// changeInfo2로 전달된 것이 player3의 참조 값이기 때문에
// 같은 참조를 하고 있던 player4의 값까지 변경된 모습입니다.
클래스의 인스턴스 식별 연산자
클래스의 인스턴스끼리 참조가 같은지 확인할 때는 식별 연산자(Identity Operators)를 사용합니다.
// 새로운 PlayerInfo2의 인스턴스를 생성해서 비교해보도록 하겠습니다.
// 현재 player3 와 player4 의 참조값은 같은 상태입니다.
var player5: PlayerInfo2 = PlayerInfo2()
print(player3 === player4) // true
print(player3 === player5) // false
print(player4 !== player5) // true
// 스위프트의 기본 데이터 타입은 모두 구조체입니다.
스위프트의 기본 데이터 타입은 모두 구조체입니다.
4. 구조체를 써야할 때
애플은 가이드라인의 조건 중 하나 이상 해당한다면 구조체를 사용하는 것을 권장합니다.
1. 연관된 간단한 값의 집합을 캡슐화하는 것만이 목적일 때
2. 캡슐화한 값을 참조하는 것보다 복사하는 것이 합당할 때
3. 구조체에 저장된 프로퍼티가 값 타입이며 참조하는 것보다 복사하는 것이 합당할 때
4. 다른 타입으로부터 상속받거나 자신을 상속할 필요가 없을 때
[출처: 야곰 SWIFT 스위프트 프로그래밍 (swift 5)]
'Mobile > Swift' 카테고리의 다른 글
[Swift - 문법] 프로퍼티(Property) - 2. 연산 프로퍼티 (0) | 2022.11.14 |
---|---|
[Swift - 문법] 프로퍼티(Property) - 1. 저장 프로퍼티 (0) | 2022.11.13 |
[Swift - 문법] 함수 (0) | 2022.11.10 |
[Swift - 문법] 반복문(for-in, while) (0) | 2022.11.09 |
[Swift - 문법] 조건문 (if , switch) (0) | 2022.11.09 |