본문 바로가기
Swift

Class와 Struct

by iOS 개린이 2022. 7. 4.

-class와 struct의 차이에 관한 질문이 정말 많다. 이 질문의 목적은 단순하게 class와 struct의 특성차이, 장*단점 등에 관해 이해하는 것이 아니라, class와 struct에 대해 정확하게 이해하여 어떤 상황이 주어졌을 때 적절하게 사용하여 프로그램 성능을 개선할 수 있는 지가 질문의 요점이라고 생각한다.

-struct는 class와 다르게 상속이 불가능하지만 상속을 제외하면 class와 기능이 유사하다. 

-struct과 class는 객체 지향 프로그래밍을 위한 필수요소로 추상화에 이용된다.

-오늘은 두 가지의 개념에 대해서 deep diving해 볼 것이다.

 

Class

-class는 청사진으로 어떤 집단의 속성과 행위를 정의해 놓은 것이다.(속성과 행위는 property와 method)

-class는 Reference Types으로 참조가 가능하다. ex) 다른 이에게 사진을 전달할 때 내 사진 파일의 원본을 참고하게 함

-class는 메모리 영역 중 힙(heap) 영역에 저장된다.

-class는 ARC로 메모리를 관리한다. (ARC는 추후에 공부예정)

-class는 상속이 가능하다. (상속은 객체지향프로그래밍의 핵심)

 

-class는 참조타입이기 때문에 참조할 필요가 없을 때 메모리에서 해제되는데, 해제되기 직전에 deInit이라는 메서드가 호출된다. (보통 메모리에서 클래스의 인스턴스가 해제되기 직전 처리해야 할 코드를 deInit 메서드에 넣어준다. deInit 메서드는 각 class당 하나만 만들 수 있고, 매개변수와 return값을 가질 수 없다. 또한 deInit 메서드를 직접 호출할 수 없다.)

 

-class는 참조하고 있는 값이 변경되었을 때 기존 값도 모두 변경된다.

ex)

class enemy {
   health : Int
   attackStrength : Int
   
   init(health : Int, attackStrength : Int) {
        self.health = health
        self.attackStrength = attackStrength
   }
   
   func takeDamage(amount : Int) {
       self.health -= amount
   }

}

let skeleton1 = enemy(health: 100, attackStrength: 10)      //class enemy 인스턴스 생성 

let skeleton2 = skeleton1                              //enemy를 참조하고 있는 skeleton1을 참조

skeleton1.takeDamage(amount: 10)                      //damage함수로 파라미터만큼 health가 깎임
print(skeleton1.health)
print(skeleton2.health)

 

-여기서 skeleton1과 2의 health값을 print 해보면, 둘 다 값이 동일하게 나온다. 이유는 skeleton1에만 takeDamege의 영향이 들어갔다고 생각할 수 있지만 class의 특성 상 skeleton2가 skeleton1을 참조하고 있기 때문이다.

 

 

여기서 주의할 점은 enemy 클래스와 관련된 것들은 모두 같은 원본을 가지고 있는 것이 아니다.

( )를 이용한 초기화로 클래스의 새로운 인스턴스를 만든다면 각자 새로운 원본들을 가지고 있는 형태가 된다.

 

ex)

class enemy {
}

let skeleton1 = enemy()
let skeleton2 = enemy()

skeleton1 == skeleton2   //이건 서로 다른 거임.

 

 

 

 

Struct

-struct는 property를 저장하거나 method를 제공하고 이를 결합해서 우리가 하나의 타입으로 사용할 수 있도록 스위프트가 제공하는 타입이다.

-struct는 class와 동일하게 instance를 만들어 사용할 수 있다. 

-struct는 value타입이다.  ex) 다른 이에게 사진을 전달할 때 사본을 만들어서 전달함, swift에서 data type(Int, String 등)이나 열거형이 모두 value타입이다. data type이 모두 struct로 구현되어 있기 때문이다. 

-struct는 stack영역에 저장된다.

 

 

class와 struct의 차이점

-class는 내부에 init() method를 통해 초기화할 때 인스턴스에 파라미터를 넣을 수 있지만, struct는 초기화 메서드없이도 자동으로 만들어준다. 그렇다고 struct가 초기화 메서드를 사용하지 못하는가? 아니다 struct도 커스텀 초기화 메서드를 만들 수 있다(자동으로 제공되는 초기화 기능은 실행되지 않음). 

-struct는 사진의 사본을 여러 개 뿌렸을 때, 그 중 한 개의 사진 내용을 변경시켜도 다른 사진들이나 원본사진에 전혀 이상이 없고, class에서는 한 개의 사진 내용을 변경시키면 참조된 모든 class가 변경되는 것이기 때문에 모든 사람들이 피해를 보게 된다.

 -class는 참조타입이기 때문에 class의 인스턴스를 let으로 선언했을 때 내부 프로퍼티 값이 var로 선언되었다면 값을 변경할 수 있다. 하지만 struct는 인스턴스도 var로 선언하고, 내부 프로퍼티도 var로 선언되었을 때 값을 변경할 수 있다. 

-class는 타입캐스팅을 통해 인스턴스의 타입을 파악할 수 있고, 구조체는 사용불가하다.

 

class, struct 인스턴스 생성 시 let과 var의 차이 

 

1. let으로 class의 인스턴스를 생성했을 때 

class Human {
    var name : String = "chacha"
}

let jiho : Human = .init()
jigo.name = "DuDu"  //아무 문제 없음!

 

-class 내부 프로퍼티를 변경시키는 것에 문제가 발생하지 않는다. (물론 프로퍼티가 var로 선언되었을 때)

 

-메모리 Stack 영역에 jiho라는 Human 인스턴스 지역변수가 저장되고, Heap 영역에는 class Human의 인스턴스가 저장된다.

여기서 지역변수 jiho가 Human 인스턴스를 참조하는 주소값을 가지고 있고, 이 주소값은 let으로 고정되어 있다. 

그래서 jiho의 주소값을 변경하는 행위는 모두 error가 발생하지만, Human 안의 변수 프로퍼티는 변경이 가능하다. 

 

2. var로 class 인스턴스를 생성했을 때

class Human {
    var name : String = "chacha"
}

var jiho : Human = .init()
jigo.name = "DuDu"  //아무 문제 없음!

 

-마찬가지로 class 내부 프로퍼티를 변경시키는 것에 문제가 발생하지 않는다.

-var 로 주소값을 고정하지 않았기때문에 jiho의 주소값을 변경하는 행위 모두 가능하다. 

 

 

3. let으로 struct 인스턴스를 생성했을 때 

Struct Human {
    var name : String = "chacha"
}

let jiho : Human = .init()
jiho.name = "DuDu"    //에러발생!!

 

-Struct 내부 프로퍼티에 접근하여 변경하는 것에 error가 발생한다. (프로퍼티가 상수든 변수든 모두 error 발생.)

 

-Struct 인스턴스 생성 시에는 스택 영역에 human 인스턴스 jiho가 저장이 된다.

그리고 let으로 주소값이 고정되어 있기 때문에 struct 내부의 프로퍼티 등의 변경이 불가능하다. 

 

 

4. var로 struct 인스턴스를 생성했을 때 

Struct Human {
    var name : String = "chacha"
}

var jiho : Human = .init()
jiho.name = "DuDu"    //문제없음!

 

-Struct 내부 프로퍼티에 접근하여 변경하는 것에 문제가 발생하지 않는다. (프로퍼티가 변수로 선언되었을 때만)

 

 

class와 struct의 적절한 사용

-애플의 가이드라인에 따르면 

 

1) 연관된 간단한 값의 집합을 캡슐화 하는 것만이 목적인 상황

2) 캡슐화 된 값이 참조되는 것보다 복사되는 것이 합리적인 상황

3) struct에 저장된 property가 value 타입이면서 복사되는 것이 합리적인 상황

4) 상속이 필요하지 않을 때

 

-위 조건에 하나 이상 부합한다면 struct을 사용하는 것이 적절하다.

-하지만 위 조건에 해당되는 상황이 아니라면 class를 사용하는 것이 적절하다.

 

class는 힙 영역에 저장되고, struct은 스택 영역에 저장된다고 했는데, 이 부분을 deep하게 들어가려면 메모리에 대한 이해가 필요하기 때문에 메모리 공부 후에 추가하는 쪽으로 진행하겠습니다.

 

 

 

참고:

Swift - 언제 class 대신 struct 를 사용하는가 - Seorenn SIGSEGV

[Swift] Class와 Struct의 차이점? (tistory.com)

[iOS / Swift] Swift 문법을 알아보자! - 8편 : 구조체 (Struct) (velog.io)

Swift - 구조체 클래스 - yagom's blog

 

 

 

 

'Swift' 카테고리의 다른 글

Protocol에 관하여  (0) 2022.11.14
lazy variables에 관하여  (0) 2022.10.07
순환참조에 관하여  (0) 2022.09.28
@escaping에 관하여  (0) 2022.08.25
getter와 setter 그리고 willSet과 didSet  (0) 2022.07.01