iOS

웹개발자의 iOS 개발기(2) - struct(구조체), class, @State, @ObservableObject

whh__ 2025. 1. 19. 13:35

struct(구조체), class, @State, @ObservableObject에 대하여 공부해 보았습니다.

01. 구조체 - 하나의 동작을 하는 객체 만들기

import SwiftUI

struct Elevator: View {
    
    //@State var level: Int = 1
    
    // 구조체 사용
    @State var myElevator = ElevatorStruct()
    
    var body: some View {
        VStack {
            //Text("층수 : \\(level)")
            Text("층수 : \\(myElevator.level)")
            
            HStack{
                Button {
                    // 함수 선언 시 입력받을 파라미터 앞에 _를 붙이면 함수 사용시 파라미터명을 입력하지 않아도 됨
                    //level = goDown(level)
                    
                    myElevator.goDown();
                } label: {
                    Text("아래로")
                }
                
                Button {
                    //level = goUp(level: level)
                    
                    myElevator.goUp();
                } label: {
                    Text("위로")
                }

            }
        }
    }
    
    // 입력받을 파라미터 앞에 _ 붙임
    func goDown(_ level: Int) -> Int {
        return level - 1
    }
    
    func goUp(level: Int) -> Int {
        return level + 1
    }
}

// 구조체 선언
struct ElevatorStruct {
    // 층 수를 표시해주는 디스플레이
    // 위로 올라갈 수 있어야 함
    // 아래로 내려갈 수 있어야 함

    var level: Int = 1
    
    mutating func goDown() {
        level = level - 1
    }
    
    mutating func goUp() {
        level = level + 1
    }
}

#Preview {
    Elevator()
}

mutating

클래스는 참조타입이기 때문에 프로퍼티 변경이 자유로운 반면,

구조체는 값타입이므로 기본적으로 메서드 내에서 인스턴스 프로퍼티를 변경할 수 없다.

메서드에서 값을 변경할 수 있도록 사용하는 키워드가 mutating 이다.

02. 클래스 - 주소값으로 객체를 만들기

import SwiftUI

struct _2_Class_Diff_: View {
    
    var myCar = Car(name: "아반떼", owner: "조우현")  // 구조체 사용
    @ObservedObject var myKar = Kar(name: "소나타", owner: "조우현2")// 클래스 사용
    
    var body: some View {
        VStack {
            
            /*
             struce(구조체)로 생성하면 callByValue
             */
            Text("struct로 생성")
            Text("\\(myCar.name)의 주인은 \\(myCar.owner)입니다")
            
            Button {
                myCar.sayHi()
            } label: {
                Text("출발")
            }
            Button {
                var broCar = myCar
                broCar.name = "물려받은 차"
                broCar.owner = "동생"
                
                myCar.sayHi()   // callByValue
            } label: {
                Text("물려주기")
            }
            
            
            /*
              class로 생성하면 callByValue
             */
            Text("class로 생성")
            Text("\\(myKar.name)의 주인은 \\(myKar.owner)입니다")
            
            Button {
                myKar.sayHi()
            } label: {
                Text("출발")
            }
            Button {
                let broCar = myKar
                broCar.name = "물려받은 차"
                broCar.owner = "동생"
                
                myKar.sayHi()   // callByReference
            } label: {
                Text("물려주기")
            }

        }
        
    }
}

// 구조체 선언
struct Car {
    var name: String
    var owner: String
    
    func sayHi() {
        print("hi \\(owner)")
    }
}

// 클래스 선언
class Kar: ObservableObject {
    @Published var name: String
    var owner: String
    
    init(name: String, owner: String) {
        self.name = name
        self.owner = owner
    }
    
    func sayHi() {
        print("hi \\(owner)")
    }
}

#Preview {
    _2_Class_Diff_()
}

  • 구조체 (struct): 값 타입 → Call by Value
  • 클래스 (class): 참조 타입 → Call by Reference

03. ObservableObject

@State

  • 변경감지 할 변수 선언 시 사용
  • View 자체가 구조체(Struct)여서 View 생성 시 멤버변수들이 자동으로 초기화된다. → 그래서 화면을 통해 변수들의 데이터를 수정해도, 화면 재로딩 시 변수의 변경사항을 기억하지 않는다. 이를 방지하기 위해 @State를 변수에 사용해 주면 해당 변수는 다른 스토리지에 저장해 두었다가 불러온다.

위의 @State와 비슷한 용도로 사용된다.

@ObservableObject

  • 변경감지 클래스 선언 시 사용
  • struct(구조체)에 사용하지 못함

@Published

  • 변경감지 클래스의 감지 대상 멤버에 사용
  • 데이터가 바뀌면 화면 재로딩