[Swift] 나는 이 이야기를 참조한다.

GUNDY·2023년 11월 20일
1

오늘의 주제는 참조와 클래스.

클래스는 데이터를 캡슐화하는 타입을 모델링하는 방법의 한 종류다.

An instance of a class is traditionally known as an object. However, Swift structures and classes are much closer in functionality than in other languages, and much of this chapter describes functionality that applies to instances of either a class or a structure type. Because of this, the more general term instance is used.

다른 언어와 달리 흔히 객체(object)라고 부르는 것을 Swift에서는 인스턴스(instance)라고 부른다. 왜냐하면 오브젝트는 클래스의 인스턴스만을 지칭하는데 Swift는 클래스와 구조체가 기능적으로 가깝기 때문에 구분하여 지칭하지 않는 것.

그래도 객체라고 불러도 문제는 없겠지만 버릇이 돼서 실수로 구조체의 인스턴스를 객체라고 부르면 틀린 표현이 된다고 생각한다.

구조체와의 공통점

이렇게까지 애플이 유사하다고 말한다면 공통점을 적을 수밖에 없다.

1. 값을 저장할 프로퍼티를 정의할 수 있다.
2. 기능을 제공할 메서드를 정의할 수 있다.
3. 서브스크립트 구문을 사용해서 값에 대한 접근을 정의할 수 있다.
4. 초기 상태를 설정할 이니셜라이저를 정의할 수 있다.
5. extension 키워드를 사용해서 타입을 확장할 수 있다.
6. 프로토콜을 채택하고 준수할 수 있다.

이 정도면 그냥 뭐 거의 전부 같은 것 아니야?

구조체와의 차이점

그런데 다른 점이 없었으면 그냥 클래스랑 구조체랑 통합했겠지 따로 썼겠어?

Classes have additional capabilities that structures don’t have

구조체에는 없는 추가 기능들도 알아보자.

1. 상속을 통해 다른 클래스의 특성을 이어받을 수 있다.

  • 특성을 이어받은 자식 클래스의 인스턴스는 부모 클래스 타입의 인스턴스로도 사용할 수 있다. 그렇기 때문에 타입에 다형성이 생길 수 있다.

2. 타입캐스팅을 통해 런타임에 클래스 인스턴스의 타입을 확인하고 해석할 수 있다.

  • 구조체에는 없는 상속이라는 기능이 있기 때문에 한 클래스 인스턴스의 타입이 특정 타입인지 확인할 수 있다.

3. 여러 곳에서 한 클래스 인스턴스를 같이 참조할 수 있다.

  • 클래스는 구조체와 달리 참조타입인데, 그 시점에 해당 인스턴스를 참조하고 있는 곳마다 참조 카운트가 1씩 증가한다.

4. 디이니셜라이저를 정의하면 할당해제 될 때 필요한 작업을 수행할 수 있다.

  • 해당 인스턴스를 참조하는 카운트가 0이되면 할당해제가 된다. 이 때 특정 작업이 필요하다면 디이니셜라이저를 정의해서 사용할 수 있다.

The additional capabilities that classes support come at the cost of increased complexity. As a general guideline, prefer structures because they’re easier to reason about, and use classes when they’re appropriate or necessary. In practice, this means most of the custom types you define will be structures and enumerations.

이 추가 기능들은 복잡성의 증가 및 성능저하의 요인이 될 수 있으므로 기본적으로 구조체를 사용할 것을 권장한다.

하지만 오늘 알아볼 주제는 클래스! 클래스가 필요한 경우도 있으므로 둘 다 공부하는 것이 좋다.

클래스 정의하기

클래스를 정의하는 방법은 class 키워드를 사용하고 전체 정의를 한 쌍의 중괄호 {} 내부에 넣는다.

class SomeClass {
    // 클래스 정의가 위치할 부분
}

이 때 주의할 점은 타입의 이름은 항상 대문자로 시작해야 한다는 점이다.

인스턴스

Television이라는 클래스가 있다고 하자.

class Television {
    
    let size: Int
    let resolution: Int
    var hasStand: Bool
    
    init(size: Int, resolution: Int, hasStand: Bool) {
        self.size = size
        self.resolution = resolution
        self.hasStand = hasStand
    }
}

이 클래스는 TV의 사이즈, 해상도, 스탠드 여부 등이 있다고 알려줄 뿐이지 각 값이 어떤지는 알 수 없다. 특정 TV를 설명하는 인스턴스를 만들려면 이니셜라이저를 사용한다.

let samsungTV = Television(size: 27, resolution: 1080, hasStand: true)

이렇게 '27인치'이며 '1080'을 지원하고 '스탠드가 달린' TV 인스턴스를 만들 수 있다.

참조 타입

값타입은 변수나 상수에 할당되거나 함수에 전달될 때 복사되는 반면 참조 타입은 복사되지 않는다.

복사본을 만드는 대신 기존 동일한 인스턴스에 대한 참조를 사용하게 된다.

여러분이 유튜브를 본다고 해봅시다.

값 타입이라면 매번 동영상을 다운로드해서 카피본을 보는 것이고

참조 타입이라면 해당 주소에 업로드 된 동영상을 보는 것이다.

결과적으로 같은 거 아냐?

원본수정이 발생하면 차이를 바로 알 수 있다.

내가 다운로드 받은 동영상은 원본이 바뀌든 안바뀌든 다운로드 받은 그 시점 그대로 유지된다.

그런데 유튜브 주소에 있는 동영상은 수정이 발생하면 같은 주소로 접근한 사람 모두가 수정본을 보게 된다.

그렇기 때문에 참조 타입의 let, var는 값 타입의 let, var와는 다르다.

인스턴스를 상수나 변수에 할당할 때 값 타입은 var로 선언한 변수만 프로퍼티를 수정할 수 있다.let은 한 번 할당하고 나면 새로 할당할 수 없고, 프로퍼티 수정으로 인해 값이 달라지는 순간 다른 인스턴스가 되기 때문이다.

그 반면에 클래스는 let으로 선언한 상수에 할당된 클래스 타입 인스턴스의 프로퍼티도 수정할 수 있는데, 해당 상수에 있는 것은 원본에 대한 참조이지 원본 인스턴스 그 자체가 아니기 때문이다.

그러니까 다른 인스턴스에 대한 참조를 발생시키려고 하는 것은 불가능하지만 원본 인스턴스에 대한 수정은 가능하단 의미다.

상수든 변수든 인스턴스의 프로퍼티에 할당된 값을 수정하려면 해당 프로퍼티가 var로 선언되어 있어야 한다.

samsungTV.hasStand = false // 원본 프로퍼티가 var로 선언되어 유효
samsungTV.size = 32 // 원본 프로퍼티가 let으로 선언되어 컴파일 에러 발생
// Cannot assign to property: 'size' is a 'let' constant

Identity Operators

두 개의 상수나 변수가 같은 클래스 인스턴스를 참조하는지는 Identity Operators로 확인할 수 있다.

  • 참조가 같다.(===)
  • 참조가 같지 않다.(!==)

물론. 값 타입은 참조를 하지 않기 때문에 당연히 이 두 연산자를 사용할 수 없다.

let anotherSamsungTV = samsungTV

print(samsungTV === anotherSamsungTV) // "true" 출력

그런데 참조가 같다는 말이 == 연산자와는 의미가 다르기 때문에 주의가 필요하다.

마무리

오늘은 클래스와 참조 타입에 대한 간단한 사항들을 알아보았다.

이 참조라는 것은 마치 내가 누구를 참조하는지 화살표를 사용해 가리키는 느낌인데,

서로를 참조할 경우 순환 참조가 발생하게 된다.

아까 참조 카운트가 0이 되면 할당 해제가 된다고 했는데, 반대로 말하자면 참조가 남아있는 이상 메모리에서 할당 해제되지 않는다는 의미이다.

그러므로 순환 참조가 발생하면 개발자가 의도하지 않은 메모리 누수가 발생할 수 있으니 각별한 주의가 필요하다.

profile
개발자할건디?

0개의 댓글