본문 바로가기
Programming/Swift

[iOS Swift5] 열거형 구조체 기본 기초

by 개발자 염상진 2024. 1. 10.

Swift에서 클래스만큼이나 자주 사용되는 타입이 바로 구조체입니다. 이번 포스팅에서는 클래스와 구조체를 비교하면서 SwiftUI를 활용한 iOS 앱 개발에 필요한 구조체와 열거형의 개념에 대해 간단히 알아보도록 하겠습니다. 

Value type VS Reference Type

Swift에서 데이터를 다루는 방식은 크게 (값 타입)Value Type(참조 타입)Reference Type이 있습니다. 차이점을 간단하게 알아보면 값 타입은 값 자체가 복사되어 전달됩니다. 이를 pass by value라고 합니다. 

반면 참조 타입은 참조가 전달되어 같은 인스턴스를 공유하게 됩니다. 이를 pass by reference라고 합니다. 즉 데이터의 값만을 전달하기 위해서는 값 타입을 사용하고, 참조를 전달해서 공유를 하기 위해서는 참조 타입을 사용하게 됩니다. 

 

 

Swift의 구조체는 값 타입이고, 클래스는 참조타입입니다. 값 타입으로 할당과 전달시 값이 그대로 복사됩니다. 실제 값이 복사되는 것이기 때문에 구조체들의 프로퍼티들은 상수로 취급되어 값을 변경할 수 없습니다. 따라서 구조체는 모델링을 위해 주로 사용되며 메모리 상에는 STACK 메모리에 저장됩니다. 인스턴스들 마다 각자의 데이터를 가질 수 있습니다.

struct MyStruct {
    var value: Int
}
var structInstance1 = MyStruct(value: 10)
var structInstance2 = structInstance1 // 복사됨
structInstance2.value = 20
print(structInstance1.value) // 출력: 10

구조체 인스턴스1을 인스턴스2에 할당했지만 두개의 인스턴스는 독립적인 데이터를 가지게 됩니다.

반면 클래스는 참조 타입입니다. 할당이나 전달 되는 경우 메모리의 주소(참조 값)이 전달됩니다. 클래스의 인스턴스 프로퍼티는 변경이 가능하며 상속을 통해 기능을 더 확장할 수 있습니다. 메모리는 힙 메모리 영역에 인스턴스가 생성됩니다. 인스턴스들은 메모리 주소를 참조하면서 데이터를 공유하게 됩니다. 

class MyClass {
    var value: Int = 10
}
var classInstance1 = MyClass()
var classInstance2 = classInstance1 // 참조가 복사됨
classInstance2.value = 20
print(classInstance1.value) // 출력: 20 (동일한 인스턴스를 참조하므로 변경 반영)

클래스의 경우 인스턴스1의 참조값을 인스턴스2와 공유하기 때문에 인스턴스2의 데이터를 변경하면 인스턴스1의 프로퍼티 값 또한 변경되어 버립니다. 

 

 

 

특징 값 타입(Value Type) 참조 타입(Reference Type)
할당 및 전달 값이 복사되어 전달 (Copy) 참조(메모리 위치)가 전달 (Reference)
메모리 위치 스택 메모리에 저장 힙 메모리에 저장
전파 방식 값이 복사되어 전파됨 (Pass by Value) 참조가 전파되어 공유됨 (Pass by Reference)
변경 가능성 기본적으로 Immutable (변경 불가능) Mutable (변경 가능)
캡슐화 데이터 캡슐화에 활용 데이터 공유와 상속에 활용
예시 struct, enum 등 class, protocol 등
예시 코드 swift struct MyStruct { var value: Int } swift class MyClass { var value: Int = 10 }

 

 

Swift 구조체 struct

Swift 구조체는 재사용가능한 코드를 작성하는데 유용한 도구입니다. class와는 위의 본 것 처럼 값이 전달되는 방식에서 차이가 있습니다. 구조체의 기본 구조는 다음과 같습니다.

struct Rectangle {
    var width: Double
    var height: Double
    
    // Initializing the struct with an initializer
    init(width: Double, height: Double) {
        self.width = width
        self.height = height
    }
    
    // Method to calculate the area of the rectangle
    func calculateArea() -> Double {
        return width * height
    }
    
    // Computed property for perimeter
    var perimeter: Double {
        return 2 * (width + height)
    }
}

클래스와 같이 프러퍼티와 메서드를 가지고 초기화 함수를 가지고 있습니다. 또한 프로토콜을 통해 사전 정의된 형태로 데이터를 구조화할 수 있습니다. 

이전 포스팅에서 Vehicle 프로토콜을 준수하는 구조체를 다음과 같이 만들 수 있습니다.

// Protocol defining basic vehicle properties
protocol Vehicle {
    var brand: String { get }
    var year: Int { get }
    
    func startEngine() -> String
}
// Struct conforming to the Vehicle protocol
struct Car: Vehicle {
    var brand: String
    var year: Int
    
    init(brand: String, year: Int) {
        self.brand = brand
        self.year = year
    }
    
    // Conforming to the startEngine method from the protocol
    func startEngine() -> String {
        return "Starting the \(brand)'s engine..."
    }
}

 

구조체는 위에서 살펴본 것 외에도 다음과 같은 특징을 가지고 있습니다. 

  • 상속을 지원하지 않는다.
  • 값 타입
  • 소멸자 메서드(deinit)을 사용하지 않는다
  • 런타임 환경에서 구조체 인스턴스 유형을 식별할 수 없다. 

실제로 앱을 개발하면서도 클래스와 구조체 중 어떤 타입을 써야 할지 고민이 되는 경우가 많은데요, 일반적으로 구조체가 클래스 타입보다는 효율적이고, 멀티 스레드 코드를 더욱 안정적으로 구현하기 때문에 가능하다면 구조체 사용이 권장되고 있습니다. 

다만 상속이 필요한 경우, 함께 공유하는 참조 형태의 인스턴스 생성이 필요한 경우, 메모리 해제 시 추가적인 리소스를 확보할 필요가 있는 경우에는 클래스 사용이 권장됩니다. 

 

 

Swift 열거형 enum

열거형은 enum 키워드를 사용합니다. enumeration의 약자로 사전 정의된 값 집합을 사용자 지정 데이터 유형으로 만들어 사용하는 타입입니다. enum 타입은 코드 블록 내부에서 switch 키워들 사용해서 조건 분기를 할 수 있습니다. 열거형은 아래처럼 사용합니다. 

enum Fruit {
    case apple
    case banana
    case orange
    case mango
}

위의 열거형을 사용하기 위해서는 switch 키워드를 사용합니다. 

func describeFruit(_ fruit: Fruit) -> String {
    switch fruit {
        case .apple:
            return "This is an apple 🍎"
        case .banana:
            return "This is a banana 🍌"
        case .orange:
            return "This is an orange 🍊"
        case .mango:
            return "This is a mango 🥭"
    }
}

print(describeFruit(Fruit.apple))

 

더욱 간단하게 enum 코드블럭 내에서 연산 프로퍼티를 사용해서 간단하게 코드를 구현할 수 있습니다. 케이스에 따라서 myFruit의 출력 결과를 미리 지정할 수 있습니다. 이 때는 switch 매개변수가 enum 자체가 되기 때문에 switch self 키워드를 사용합니다. 

enum Fruit {
    case apple
    case banana
    case orange
    case mango
    
    var myFruit: String {
        switch self {
        case .apple:
            return "This is an apple 🍎"
        case .banana:
            return "This is a banana 🍌"
        case .orange:
            return "This is an orange 🍊"
        case .mango:
            return "This is a mango 🥭"
        }
    }
}

fruit이라는 상수를 만들어주고, Fruit 열거형 타입 어노테이션을 붙여줍니다. 그리고 케이스를 지정해 줍니다. 이제 사전 선언한 연산 프로퍼티를 출력하면 결과물을 확인할 수 있습니다. 

let fruit: Fruit = .mango

print(fruit.myFruit) // This is a mango 🥭

 

 

 

🚀️ 도움이 되셨다면 구독좋아요 부탁드립니다 👍️

 

 

 

[iOS Swift5] 함수, 메서드 그리고 클로져 기본 개념

프로그래밍 영역에서 함수는 반복적인 작업을 대폭 줄여주는 장점이 있습니다. 애플의 스위프트 언어 또한 함수를 제공하고 있는데요, 이번 포스팅에서는 Swift의 함수에 대해 알아보도록 하겠

about-tech.tistory.com

 

 

[iOS Swift5] if else, guard 제어 흐름 관리하기

Swift5 제어흐름 관리하기 안녕하세요 About Tech 입니다. 이번 포스팅에서는 Swfit 언어에서 제어흐름을 어떻게 관리할 수 있는지 알아보고자 합니다. 제어흐름은 조건식이라고도 불리는 if 문이나 gu

about-tech.tistory.com

 

 

[iOS Swift5] 연산자와 표현식

Swift 연산자와 표현식 변수에 값을 할당하는 방법은 기본적으로 표현식을 따릅니다. 피연산자와 할당자 그리고 연산자를 통해 값을 생성하고 변수에 데이터를 저장할 수 있습니다. var number = 1 +

about-tech.tistory.com

 

댓글