[Swift] Ch9. 구조체와 클래스

JJUDEV·2024년 3월 14일
0

Swift

목록 보기
10/21
post-thumbnail

본 글은 야곰의 스위프트 프로그래밍: Swift5 교재를 토대로 공부한 내용과 찾아본 내용을 요약한 것입니다.

객체지향 프로그래밍 패러다임을 안다면 클래스라는 용어를 들어봤을 것입니다. 그리고 데이터를 구조화하여 관리하는 데 구조체를 사용해봤을 것입니다. 스위프트에서는 구조체와 클래스의 모습과 문법이 거의 흡사합니다. 다만, 구조체의 인스턴스는 값 타입이고, 클래스의 인스턴스는 참조 타입이라는 이 둘의 가장 큰 차이점입니다.

9.1 구조체

1) 구조체의 정의

구조체를 정의한다는 것은 새로운 타입을 생성해주는 것과 마찬가지이므로 기본 타입 이름(Int, String, Bool 등)과 마찬가지로 대문자 카멜케이스를 사용하고, 프로퍼티메서드소문자 카멜케이스를 사용합니다.

struct Car {
    // 프로퍼티 정의
    var make: String // 제조사
    var model: String // 모델
    var year: Int // 연식

    // 메서드 정의
    func printDetails() {
        print("제조사: \(make), 모델: \(model), 연식: \(year)")
    }
}

// 구조체 인스턴스 생성
let myCar = Car(make: "Hyundai", model: "Sonata", year: 2020)

// 인스턴스 메서드 호출
myCar.printDetails()

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

  • 멤버와이즈 이니셜라이저

멤버와이즈 이니셜라이저는 구조체에 정의된 모든 저장 프로퍼티(stored properties)에 대해 자동으로 생성됩니다. 각 프로퍼티의 이름과 타입을 이용하여, 초기값을 설정할 수 있는 매개변수를 가진 이니셜라이저가 만들어집니다. 선택적 프로퍼티(optional properties)나 기본값을 가진 프로퍼티는 이니셜라이저의 매개변수에서 생략될 수 있습니다.

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

// 멤버와이즈 이니셜라이저를 사용하여 인스턴스 생성
let person = Person(name: "John Doe", age: 30)

위 예제에서 Person 구조체는 name과 age라는 두 개의 저장 프로퍼티를 가지고 있습니다. Swift는 이 구조체에 대해 자동으로 멤버와이즈 이니셜라이저를 제공합니다. 따라서 Person(name: "John Doe", age: 30)과 같이 새로운 Person 인스턴스를 생성할 때, 모든 프로퍼티에 대한 초기값을 제공할 수 있습니다.

  • 사용자 정의 이니셜라이저

Swift에서 사용자 정의 이니셜라이저(custom initializer)를 작성하는 것은 객체의 초기 상태를 설정하는 데 필요한 특정 로직을 구현할 수 있게 해줍니다. 클래스(class), 구조체(struct), 그리고 열거형(enumeration)에서 사용자 정의 이니셜라이저를 정의할 수 있습니다. 이니셜라이저를 통해 인스턴스가 생성될 때 필요한 초기값을 제공하거나 초기 설정 작업을 수행할 수 있습니다.

struct Book {
    var title: String
    var author: String

    // 사용자 정의 이니셜라이저
    init(title: String, author: String) {
        self.title = title
        self.author = author
    }
}

위 예제에서 Person 구조체는 name과 age라는 두 개의 프로퍼티를 가지고 있으며, 사용자 정의 이니셜라이저를 통해 이 프로퍼티들을 초기화합니다. self 키워드는 이니셜라이저 내에서 인스턴스 자신을 참조하는 데 사용됩니다.

9.2 클래스

1) 클래스 정의

스위프트의 클래스는 부모클래스가 없더라도 상속 없이 단독으로 정의가 가능합니다.

class Car {
    var color: String // 저장 프로퍼티
    var speed: Int = 0

    // 이니셜라이저(생성자)
    init(color: String) {
        self.color = color
    }

    // 메서드(함수)
    func accelerate(by amount: Int) {
        speed += amount
    }

    func brake() {
        speed = 0
    }
}

2) 클래스 인스턴스의 생성 및 초기화

Swift에서 클래스 인스턴스의 생성 및 초기화는 인스턴스를 생성할 때 클래스에 정의된 프로퍼티와 메서드를 준비하는 과정입니다. 이 과정은 init 메서드, 즉 이니셜라이저를 통해 수행됩니다. 이니셜라이저는 클래스, 구조체, 열거형 등의 인스턴스가 생성될 때 초기 상태를 설정하는 특별한 메서드입니다.

  • 기본 이니셜라이저

클래스를 정의할 때 특별한 이니셜라이저를 제공하지 않으면, Swift는 기본 이니셜라이저를 제공합니다. 기본 이니셜라이저는 모든 프로퍼티가 기본값을 가지고 있을 때 사용할 수 있습니다.

class Vehicle {
    var numberOfWheels = 0
    var description: String {
        return "\(numberOfWheels) wheel(s)"
    }
}

let vehicle = Vehicle()
print(vehicle.description) // "0 wheel(s)" 출력
  • 프로퍼티가 기본값을 가지고 있지 않은 경우의 예시
class ExampleClass {
    var propertyWithoutDefaultValue: String
    // 이 클래스에는 기본 이니셜라이저가 자동으로 제공되지 않습니다.
    // 따라서, 사용자 정의 이니셜라이저를 제공해야 합니다.
    
    init(propertyWithoutDefaultValue: String) {
        self.propertyWithoutDefaultValue = propertyWithoutDefaultValue
    }
}

이런 방식은 클래스가 올바르게 초기화되도록 강제하며, 인스턴스가 사용되기 전에 모든 프로퍼티가 유효한 값으로 설정되었는지 확인하는 데 도움이 됩니다.

  • 사용자 정의 이니셜라이저

클래스에 하나 이상의 사용자 정의 이니셜라이저를 정의할 수 있습니다. 이를 통해 인스턴스 생성 시 특정 값을 설정하거나 필요한 초기화 로직을 수행할 수 있습니다.

class Bicycle: Vehicle {
    override init() {
        super.init()
        numberOfWheels = 2
    }
}

let bicycle = Bicycle()
print(bicycle.description) // "2 wheel(s)" 출력
  • 이니셜라이저 매개변수

이니셜라이저에는 매개변수를 추가하여, 인스턴스 생성 시 특정 값을 전달할 수 있습니다. 이 방법은 인스턴스의 초기 상태를 더 유연하게 설정할 수 있게 해줍니다.

class Car: Vehicle {
    var color: String
    
    init(color: String) {
        self.color = color
        super.init()
        numberOfWheels = 4
    }
}

let car = Car(color: "Blue")
print(car.description) // "4 wheel(s)" 출력
print("Car Color: \(car.color)") // "Car Color: Blue" 출력

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

클래스의 인스턴스는 참조 타입이므로 더는 참조할 필요가 없을 때 메모리에서 해제됩니다. 이 과정을 소멸이라고 하는데 소멸되기 직전 deinit라는 메서드가 호출됩니다. 클래스 내부에 deinit 메서드를 구현해주면 소멸되기 직전 deinit 메서드가 호출됩니다.

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

deinit 메서드에는 인스턴스 소멸 전에 데이터를 저장한다거나 다른 객체에 인스턴스 소멸을 알려야 할 때와 같이 메모리에서 해제되기 직전에 처리할 코드를 넣어줍니다.

9.3 구조체와 클래스의 차이

  • 같은점

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

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

1) 값 타입과 참조 타입

Swift에서 데이터 타입은 크게 값 타입(Value Type)과 참조 타입(Reference Type)으로 나뉩니다. 이 두 타입의 주요 차이점은 데이터를 할당할 때 또는 함수와 메서드에 전달할 때 데이터가 어떻게 전달되는지에 있습니다.

  • 값 타입(Value Type)

값 타입은 데이터를 전달할 때 값의 복사본을 생성하여 전달합니다. 즉, 원본과 복사본은 완전히 독립적인 데이터를 가지게 됩니다. Swift에서는 구조체(struct), 열거형(enum), 기본 데이터 타입(Int, String, Array, Dictionary 등)이 값 타입에 속합니다.

struct Point {
    var x: Int
    var y: Int
}

var originalPoint = Point(x: 10, y: 20)
var copiedPoint = originalPoint // 값의 복사가 일어납니다.

copiedPoint.x = 30 // 복사본의 값을 변경합니다.

// 원본은 변경되지 않습니다.
print(originalPoint) // 출력: Point(x: 10, y: 20)
print(copiedPoint) // 출력: Point(x: 30, y: 20)
  • 참조 타입(Reference Type)

참조 타입은 데이터를 전달할 때 데이터의 참조(즉, 메모리 주소)가 전달됩니다. 따라서, 원본과 복사본이 같은 데이터 인스턴스를 가리키게 됩니다. Swift에서 클래스(class)는 참조 타입에 속합니다.

class Person {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

var originalPerson = Person(name: "Alice")
var referencedPerson = originalPerson // 참조의 복사가 일어납니다.

referencedPerson.name = "Bob" // 참조한 인스턴스의 값을 변경합니다.

// 원본도 함께 변경됩니다.
print(originalPerson.name) // 출력: Bob
print(referencedPerson.name) // 출력: Bob

2) 스위프트의 기본 데이터 타입: 구조체

public struct String {
	/// An empty 'String'.
    public init()
}

swift의 기본 타입 (Bool, Int, Array, Dictionary, Set, String 등등) 모두 구조체로 구현되어 있습니다. 전달인자를 통해 데이터를 전달하면 모두 값이 복사되어 전달될 뿐, 함수 내부에서 아무리 전달된 값을 변경해도 기존의 변수나 상수에는 전혀 영향을 미치지 못합니다.

9.4 구조체와 클래스 선택해서 사용하기

아래와 같은 경우 구조체를 사용하는 것을 권장합니다.

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

참고자료

  • 스위프트 프로그래밍 3판(지은이: 야곰, 출판사: 한빛미디어(주))
  • ChatGPT 4
profile
4년차 앱개발자입니다.

0개의 댓글