[Swift 문법] 클래스(Class) vs 구조체(Struct) (1)

Yellowtoast·2023년 12월 4일
0

Swift

목록 보기
7/11
post-thumbnail

해당 글은 iOS 스터디를 위한 기본 문법을 정리한 글 입니다.
패스트캠퍼스 강의 및 Advanced Swift (by Chris Eidhof) 책의 내용을 참고하여 작성하였습니다.

값 타입과 참조 타입

구조체는 값 유형이고 클래스는 참조 유형입니다.

값 타입(Value Type)

값 타입은 변수와 값 사이의 이러한 직접적인 관계, 즉 값(값 타입의 인스턴스라고도 함)이 메모리 위치에 직접 존재한다는 특징이 있습니다.

var a:Int = 3 
var b=a 
b+=1

아래 예제에서 메모리에서 현재 3이라는 값을 담고 있는 Int 유형의 위치를 나타내기 위해 a라는 이름을 사용했습니다. 두 번째 변수인 b는 메모리 내 다른 위치의 이름으로, 마찬가지로 Int 유형이며 초기 할당 후 값 3을 포함하고 있습니다. b += 1 문은 b라고 하는 메모리 위치에 저장된 값을 가져와서 1씩 증가시킨 다음 메모리의 같은 위치에 다시 씁니다. 따라서 b는 이제 4라는 값을 포함합니다. b를 증가시키기는 했지만, 해당 코드는 b 변수의 값만 수정하므로 a는 이 문에 영향을 받지 않습니다.

이는 정수와 같은 단순한 값 유형뿐만 아니라 Struct와 같은 더 복잡한 유형에도 적용됩니다.

참조 타입(Reference Type)

이번에는 참조 타입에 대해 알아보겠습니다.

var view1 = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) var view2 = view1
view2.frame.origin = CGPoint(x: 50, y: 50)
view1.frame.origin // (50, 50)
view2.frame.origin // (50, 50)

view2.frame.origin에 새 원점을 할당했지만 당연히 view1의 프레임도 변경되었을 것으로 예상합니다. 사실 뷰1과 뷰2는 화면에 표시되는 동일한 뷰를 나타냅니다. UIView는 참조 유형이며, view1과 view2 변수는 메모리에서 동일한 기본 UIView 인스턴스를 가리키는 참조를 포함한다고 말할 수 있습니다.

view2 = UILabel()

이렇게 하면 view1은 여전히 이전에 만든 보기를 참조하는 반면 view2는 이제 새로 만든 레이블 인스턴스를 참조합니다. 다시 말해, 재할당으로 인해 view2가 가리키는 인스턴스(또는 객체)가 변경되었습니다.

이것이 참조 유형의 본질입니다. 변수는 '사물' 자체(예: UIView 또는 URLSession의 인스턴스)를 포함하지 않고 그것에 대한 참조를 포함합니다. 값 타입 변수는 값 자체를 포함하지만 참조 타입 변수는 다른 곳에 있는 값을 가리키는 참조를 포함합니다.

이러한 방향성을 통해 프로그램의 여러 부분에서 객체에 대한 액세스를 공유할 수 있습니다.

클래스와 구조체의 비교

그렇다면, 값 타입과 참조 타입의 차이를 직접 클래스와 구조체를 만들어보면서 비교해보겠습니다.

클래스

class ScoreClass {
  var home: Int
  var guest: Int
  init(home: Int, guest: Int) {
  self.home = home
  self.guest = guest
  }
}
var score1 = ScoreClass(home: 0, guest: 0) var score2 = score1
score2.guest += 1
score1.guest // 1

score1과 score2 변수는 모두 동일한 메모리 주소를 참조합니다. 따라서 score2를 통해 게스트의 점수를 변경하면 score1을 통해 게스트의 점수에 액세스할 때 표시되는 값도 변경됩니다.
아래와 같이 클래스를 변경하는 함수에 score2를 전달하여 변경할 수도 있습니다.

func scoreGuest(_ score: ScoreClass) { 
	score.guest += 1
}

scoreGuest(score1) 
score1.guest // 2
score2.guest // 2

구조체

struct ScoreStruct {
  var home: Int
  var guest: Int
}
var score3 = ScoreStruct(home: 0, guest: 0) var score4 = score3
score4.guest += 1
score3.guest // 0

구조체를 다른 변수에 할당하면 값의 독립적인 복사본이 생성됩니다. 따라서 score4 변수를 통해 게스트의 점수를 변경해도 score3의 게스트 점수에는 아무런 영향을 미치지 않습니다.

Score의 구조체 버전을 사용하면 위에서 클래스에 했던 것과 같은 scoreGuest 함수를 정의할 수 없습니다. 왜일까요?

  1. 값 유형을 함수의 매개변수로 전달하면 변수에 할당할 때와 마찬가지로 값의 독립적인 복사본이 생성됩니다.
  2. 함수 매개변수는 함수 내에서 불변이므로(let을 사용해 선언한 변수처럼) 그 속성을 변경할 수 없습니다.
    • 비슷한 함수를 만들려면 다음 섹션에서 다룰 inout 매개변수를 사용해야 합니다.

클래스가 더 강력한 도구이긴 하지만, 그 기능에는 대가가 따릅니다. 반면에 구조체는 훨씬 더 제한적이지만 이러한 제한이 오히려 이점이 될 수도 있습니다.

다음 글에서는 클래스와 구조체를 변경(Mutation)의 관점에서 다뤄보도록 하겠습니다.

profile
Flutter App Developer

0개의 댓글