iOS

[Swift] 스위프트 프로그래밍 - Part2 - 9 : 구조체와 클래스

건복치 2021. 4. 21. 18:30
반응형
야곰님의 스위프트 프로그래밍 책으로 공부한 내용을 잊어버리지 않게 간단하게 정리한 글입니다.

관련 포스트

더보기

책 범위 : Part 2. 객체지향 프로그래밍과 스위프트 - 9. 구조체와 클래스


구조체와 클래스

- 데이터를 용도에 맞게 묶어 표현하고자 할 때 유용

- 프로퍼티와 메서드를 사용해 구조화된 데이터와 기능을 가질 수 있다.

- 하나의 새로운 사용자 정의 데이터 타입을 만드는 것.

- 구조체와 클래스의 모습과 문법은 비슷하지만, 구조체의 인스턴스는 값 타입, 클래스의 인스턴스는 참조 타입이라는 것이 가장 큰 차이점.

- 소스파일 하나에 여러 개의 구조체와 클래스를 정의하고 구현해도 문제없음.

- 중첩 함수와 마찬가지로 구조체 안에 구조체, 클래스 안에 클래스 등과 같이 중첩 타입의 정의 및 선언이 가능

구조체

1. 구조체 정의

- struct 키워드로 정의

 

struct 구조체 이름 {
	프로퍼티와 메서드들
}

 

struct BasicInformation {
    var name: String
    var age: Int
}

2. 구조체 인스턴스 생성 및 초기화 

- 구조체 정의 후, 인스턴스를 생성하고 초기화하고자 할 때는 기본적으로 생성되는 멤버와이즈 이니셜라이저를 사용한다.

- 구조체에 기본 생성된 이니셜라이저의 매개변수는 구조체의 프로퍼티 이름으로 자동 지정된다. 

- 인스턴스가 생성되고 초기화된 후 프로퍼티 값에 접근하고 싶다면 마침표(.) 사용

- 구조체를 상수 let으로 선언하면 프로퍼티 값을 변경할 수 없고, 변수 var로 선언하면 변경 가능

- 기본 제공되는 멤버와이즈 이니셜라이저 외에 사용자 정의 이니셜라이저도 구현 가능 

 

var minhaInfo: BasicInformation = BasicInformation(name: "minha", age: 44)
minhaInfo.age = 100 // 변경 가능
minhaInfo.name = "amelia" // 변경 가능

let ameliaInfo: BasicInformation = BasicInformation(name: "amelia", age: 100)
//ameliaInfo.age = 100 // 변경 불가 Error
//ameliaInfo.name = "minha" // 변경 불가 Error

클래스

1. 클래스의 정의

- class 키워드 사용 

- 클래스는 상속받을 수 있기에 상속받을 때는 클래스 이름 뒤에 콜론(:)을 써주고 부모 클래스 이름을 명시한다.

 

class 클래스 이름 {
	프로퍼티와 메서드들
}

class 클래스 이름 : 부모클래스 이름 {
	프로퍼티와 메서드들
}

 

class Person {
    var height: Float = 0.0
    var weight: Float = 0.0
}

2. 클래스 인스턴스 생성과 초기화

- 클래스 정의 후, 인스턴스를 생성하고 초기화하고자 할 때는 기본적인 이니셜라이저를 사용

- 프로퍼티의 기본값이 지정되어 있다면 전달 인자를 통하여 따로 초깃값을 전달해주지 않아도 됨

- 인스턴스가 생성되고 초기화된 후 프로퍼티 값에 접근하고 싶다면 마침표(.) 사용

- 구조체와 달리 클래스의 인스턴스는 참조 타입이므로 클래스의 인스턴스를 let으로 선언해도 내부 프로퍼티 값을 변경할 수 있음

- 사용자가 직접 이니셜라이저를 정의할 수도 있음 

 

var minha: Person = Person()
minha.height = 123.4
minha.weight = 123.4

let amelia: Person = Person()
amelia.height = 567.8
amelia.weight = 567.8

 

* 다른 프로그래밍 언어에서는 클래스의 인스턴스를 객체라고 부름.

하지만 스위프트 공식 문서에서는 좀 더 한정적인 인스턴스라는 용어 사용.

3. 클래스 인스턴스의 소멸

- 클래스의 인스턴스는 참조 타입이므로 더는 참조할 필요가 없을 때 메모리에서 해제된다 = 인스턴스의 소멸

- 소멸되기 직전 디이니셜라이저라고 부르는 deinit 메서드 호출됨

- 클래스당 하나만 구현할 수 있으며, 매개변수와 반환 값을 가질 수 없음 (매개변수를 위한 소괄호조차 X)

- 주로 인스턴스가 메모리에서 해제되기 직전에 처리할 코드를 넣어줌 

 

class Person {
    var height: Float = 0.0
    var weight: Float = 0.0
    
    deinit {
        print("Person 클래스의 인스턴스가 소멸됨 ")
    }
}

var person: Person? = Person()
person = nil // Person 클래스의 인스턴스가 소멸됨

구조체와 클래스의 차이

같은 점

  • 값을 저장하기 위해 프로퍼티를 정의할 수 있음
  • 기능 실행을 위해 메서드를 정의할 수 있음
  • 서브스크립트 문법을 통해 구조체 또는 클래스가 갖는 값(프로퍼티)에 접근하도록 서브스크립트를 정의할 수 있음
  • 초기화될 때의 상태를 지정하기 위해 이니셜 라이저를 정의할 수 있음
  • 새로운 기능 추가를 위해 익스텐션을 통해 확장할 수 있음
  • 특정 기능을 실행하기 위해 특정 프로토콜을 준수할 수 있음

차이점

  • 구조체는 상속할 수 없음
  • 타입 캐스팅은 클래스의 인스턴스에만 허용됨
  • 디이니셜라이저는 클래스의 인스턴스에만 활용 가능
  • 참조 횟수 계산은 클래스의 인스턴스에만 적용됨

1. 값 타입과 참조 타입

구조체는 값 타입이고 클래스는 참조 타입이다.

둘의 가장 큰 차이는 '무엇이 전달되느냐'이다.

 

- 예를 들어 함수의 전달 인자로 값 타입의 값을 넘긴다면 전달될 값이 복사되어 전달됨.

참조 타입이 전달 인자로 넘겨지면 값을 복사하지 않고 참조(주소)가 전달됨.

참조 타입은 함수의 전달 인자로 넘길 때, 다른 변수 또는 상수에 할당될 때도 참조가 전달되고 할당됨.

 

- 값 타입의 데이터를 함수의 전달 인자로 전달하면 메모리에 전달 인자를 위한 인스턴스가 새로 생성된다.

생성된 새 인스턴스에는 전달하려는 값이 복사되어 들어간다.

 

- 참조 타입의 데이터는 전달 인자로 전달할 때 기존 인스턴스의 참조를 전달하므로

새로운 인스턴스가 아니라 기존의 인스턴스 참조를 전달한다.

함수의 전달 인자뿐만 아니라 새로운 변수에 할당될 때 또한 마찬가지다.

 

var myInfo: BasicInformation = BasicInformation(name: "minha", age: 100)
myInfo.age = 44

var yourInfo: BasicInformation = myInfo // myInfo의 값을 복사하여 할당함

print("my age : \(myInfo.age)") // 44
print("your age : \(yourInfo.age)") // 44

yourInfo.age = 99

print("my age : \(myInfo.age)") // 44
print("your age : \(yourInfo.age)") // 99 - yourInfo는 myInfo의 값을 복사해왔기 때문에 별개의 값을 갖는다.


var man: Person = Person()
var woman: Person = man

print("man height : \(man.height)") // 0.0
print("woman height : \(woman.height)") // 0.0

woman.height = 180.8
print("man height : \(man.height)") // 180.8 - woman은 man을 참조하기 때문에 값이 변동됨
print("woman height : \(woman.height)") // 180.8 - man이 참조하는 곳과 woman이 참조하는 곳이 같음을 알 수 있음


func changeBasicInfo(_ info: BasicInformation) {
    var copiedInfo: BasicInformation = info
    copiedInfo.age = 1
}

func changePersonInfo(_ info: Person) {
    info.height = 200.2
}

// changeBasicInfo(_:)로 전달되는 전달인자는 값이 복사되어 전달되기 때문에 myInfo의 값만 전달됨
changeBasicInfo(myInfo)
print("my age : \(myInfo.age)") // 44

// changePersonInfo(_:)로 전달인자로 man의 참조가 전달되었기 때문에 man이 참조하는 값들에 변화가 생김
changePersonInfo(man)
print("man height : \(man.height)") // 200.2

 

- 클래스의 인스턴스끼리 참조가 같은지 확인할 때는 식별 연산자를 사용한다.

 

var me: Person = Person()
let friend : Person = me
let anotherFriend: Person = Person()

print(me === friend) // true
print(me === anotherFriend) // false
print(friend !== anotherFriend) // true

2. 스위프트의 기본 데이터 타입은 모두 구조체

- 스위프트의 기본 타입 (Bool, Int, Array, Dictionary, Set...)은 모두 구조체로 구현되어 있다.

- 기본 데이터 타입은 모두 값 타입이다.

- 전달 인자를 통해 데이터를 전달하면 모두 값이 복사되어 전달될 뿐, 함수 내부에서 아무리 전달된 값을 변경해도 기존의 변수나 상수에는 전혀 영향을 미치지 못한다.

구조체 vs 클래스

애플은 가이드라인에서 다음 조건 중 하나 이상에 해당한다면 구조체를 사용하는 것을 권장한다.

 

  • 연관된 간단한 값의 집합을 캡슐화하는 것만이 목적일 때
  • 캡슐화한 값을 참조하는 것보다 복사하는 것이 합당할 때
  • 구조체에 저장된 프로퍼티가 값 타입이며 참조하는 것보다 복사하는 것이 합당할 때
  • 다른 타입으로부터 상속받거나 자신을 상속할 필요가 없음

참고

아래를 참고해 정리한 내용입니다.

 

스위프트 프로그래밍 3판(야곰) - 한빛미디어
반응형