enum, class, struct

hyun·2025년 5월 27일
6

iOS

목록 보기
6/54

 열거형(enum)

정해진 값들 중에서 하나를 고를 수 있게 그룹으로 묶어놓은 것
예를들어 ) 봄, 여름, 가을, 겨울

👉🏻 정해진 선택지들끼리 하나의 상자에 묶어놓은 것

왜 쓰는 걸까?

ex) 어떤 앱을 만들고 있는데 사용자의 계절을 선택해야 한다고 가정했을 때
그냥 문자열로 spring, summer 이렇게 쓰면 오타가 날 수도 있고, 없는 계절을 넣을 수도 있음
👉🏻 열거형을 쓰면 정해진 값만 쓸 수 있게 해서 더 안전하고 실수도 줄일 수 있음

어떻게 쓸까?

var currentSeason = Season.spring

이렇게 하면 현재 계절이 봄이라고 저장됨
Season.spring처럼 열거형이름.값이름 형식으로 쓰는데,
앞에 Season을 생략하고 .spring만 써도 괜찮을 때도 있음 (이미 어떤 열거형인지 컴퓨터가 알 때)

enum 기능

  1. 연관값 (Associated Value)
    열거형의 값이 이름도 저장할 수 있지만 추가 정보 저장 가능함

ex) 택배 상태를 나타내는 열거형이 있다고 할 때
그중 <배송중>에는 송장번호가 필요할 수도 있을텐데
그럴 때 <배송중>이라는 상태에 송장번호를 함께 넣을 수 있음

👉🏻 어떤 값을 고르면서 거기에 정보를 하나 더 붙여서 저장할 수 있는 것

  1. 원시값 (Raw Value)
    반대로, 어떤 값에 기본 숫자나 글자 같은 값을 붙이고 싶을 때

예를 들어 월을 열거형으로 만들면
1월 = 1, 2월 = 2 이런 식으로 숫자를 붙이고 싶을 수 있음 근데 붙이나? 암튼 있다고 했을 때

👉🏻 그럴 땐 열거형의 각 값에 숫자나 문자열 같은 기본값을 줄 수 있음
자동으로 붙기도 하고, 직접 정해줄 수도 있음

  1. enum 안에 함수도 넣을 수 있음
    열거형이 상황에 따라 어떤 말을 하게 하고 싶을 때

    ex) 봄이면 따뜻함, 겨울이면 추움 이런 말

👉🏻 그럴 땐 열거형 안에 함수를 만들어서 상황에 따라 다른 말을 하게 할 수 있음
즉, 그냥 선택지만 있는 게 아니라 각 선택지가 자기 역할도 하게 만드는 것인 셈

enum도 인스턴스가 될 수 있음

인스턴스 : 진짜로 만들어진 것(?)

음.. 예를들어 설계도만 있다고 해서 자동차가 당장 막 굴러 갈 수 있는 건 아님.
설계도를 보고 진짜 자동차를 뚝딱뚝딱 만드는 게 인스턴스임

마찬가지로 열거형도 설계도고,
거기서 .spring 같은 실제 값을 만들어서 쓰는 게 인스턴스

요약

개념쉽게 말해
enum정해진 선택지들을 그룹으로 묶은 것
case그 그룹 안의 하나하나의 선택지
연관값선택지에 추가로 붙는 정보
원시값선택지에 기본적으로 지정되는 값 (숫자나 글자 등)
함수선택지가 자기가 할 말을 정해서 말하는 기능
인스턴스설계도(enum)를 가지고 만든 진짜 값

 클래스(class)

클래스는 설계도.
무언가를 만들기 위한 설계도처럼, 클래스는 이런 정보를 가지고 이런 행동을 해야함. !!! 라고 미리 정해놓는 거
예를들어 ) 학생을 생각해봤을 때
학생은 이름이 있고 나이가 있음.
공부를 하거나, 시험을 볼 수도 있음
뭐 이런 여러 가지 것들을 코드로 만들기 위해 클래스가 필요한 것임. (분할??의 개념인가)

어떤 걸 담고 있나

프로퍼티 (Property)

→ 정보 저장 (ex. 이름, 나이)

메소드 (Method)

→ 어떤 행동 (ex. 공부하기, 시험보기)

사용 방법

클래스 정의 (설계도 만들기)
인스턴스 만들기 (설계도를 보고 학생 하나 만들기)
만든 학생한테 이름도 넣고, 행동도 시켜볼 수 있음

만들 때 중요한 거 — init

클래스를 만들 땐, 이 학생은 이름이 뭔지, 몇 살인지 알려줘야 함
그래서 처음 만들 때 초기화(init) 라는 걸 해줘야 함

init(name: String, age: Int) {
    self.name = name
    self.age = age
}

기능

✅ 값 복사X, 공유O
학생 A가 있고, 그걸 학생 B한테 복사했어도, A랑 B는 같은 학생임
그니까 만약 B가 이름을 바꾸면, A의 이름도 같이 바뀌는 것
왜냐면 같은 학생(같은 주소의 데이터)을 가리키고 있기 때문

✅ 상속 가능
학생이라는 클래스를 만들고,
그걸 중심으로 중학생, 고등학생을 만들 수 있음
중학생은 학생의 성질을 그대로 물려받아서 사용할 수 있음 (이게 상속)

✅ 클래스는 소멸될 때 자동으로 deinit이 실행됨
학생이 졸업해서 학교를 떠나면 그 학생 정보를 메모리에서 없애야 할텐데
그때 자동으로 불리는 함수가 deinit. (개발자는 못부를 걸..? 자동으로 호출)

정리

개념쉽게 말하면
클래스설계도, 붕어빵 틀
인스턴스진짜 붕어빵 (클래스로 만든 실체)
프로퍼티정보 (이름, 나이, 학교 등)
메소드행동 (공부하기, 놀기 등)
init붕어빵 만들 때 재료 넣기
deinit붕어빵 먹고 틀 비우기
Reference Type같은 붕어빵을 나눠 먹는 느낌 (같은 걸 가리킴)
상속붕어빵 틀을 물려받아서 새로운 틀 만들기

 구조체(struct)

구조체는 물건 박스
어떤 정보를 담고 싶을 때 쓰는 박스 같은 거
예를들어 ) 사람이라는 정보를 담는 박스를 만들 수 있음

구조체 안에는

프로퍼티 (Property)

→ 정보를 담는 변수들 (예: 이름, 나이)

메소드 (Method)

→ 구조체가 할 수 있는 행동 (예: 자기소개하기)

구조체 특징

구분설명
프로퍼티 & 메소드정보를 저장하고, 행동을 정의할 수 있음
인스턴스 생성 가능구조체() 로 실제로 사용할 수 있음
기본값 설정 가능변수에 기본값 줄 수 있음
멤버와이즈 이니셜라이저 제공init 안 써도 자동으로 초기화 함수 만들어줌
값 타입복사하면 서로 완전히 다른 애가 됨
상속 불가클래스처럼 물려주거나 물려받을 수 없음
let으로 만들면 프로퍼티 수정 불가값 바꾸려면 var로 만들어야 됨
mutating 함수 필요프로퍼티를 바꾸는 함수는 mutating 키워드 필수

만드는 방법

struct Person {
    var name: String
    var age: Int
    
    func introduce() {
        print("나는 \(name)이고 \(age)살 이당")
    }
    
    mutating func happyNewYear() {
        age += 1
    }
}

ex )

var me = Person(name: "가현", age: 20)
me.introduce() 
me.happyNewYear()
// age가 21로 증가

구조체 초기화 방법 (init)

1. 멤버와이즈 이니셜라이저 (자동 생성)

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

let brody = Person(name: "가현", age: 10)
// init 만들지 않아도 자동으로 name과 age를 넣는 생성자 만들어줌

2. 기본값 사용하기

struct Person {
    var name: String
    var age: Int = 0 // 기본값

    func introduce() {
        print("나는 \(name)이고 저는 \(age)살 이당")
    }
}

let a = Person(name: "가현")
// 출력: 나는 가현이고 0살 이당

3. 직접 init 만들기

struct Person {
    var name: String
    var age: Int

    init() {
        self.name = "이름없음"
        self.age = 0
    }
}

직접 init()을 만들면 자동 생성되는 멤버와이즈 init은 사라짐

구조체는 값 타입이라 복사하면 서로 독립적인 다른 값임 !!!!!!!!!!!!!!
→ A를 복사해서 만든 B는, A랑 아무 관련이 없음
구조체 안에서 프로퍼티를 바꾸는 함수는 반드시 mutating 키워드를 붙여야 함

클래스 vs 구조체 비교 요약

항목클래스구조체
타입참조 타입값 타입
복사 시같은 걸 가리킴 (공유됨)새로 복사됨 (독립됨)
상속가능불가능
init 자동 생성없음있음 (멤버와이즈 init)
mutating 키워드필요 없음필요함 (프로퍼티 수정 시)
deinit있음없음

 struct

데이터와 행동을 묶은 사용자 정의 데이터 타입

특징

항목설명
구성 요소프로퍼티(변수, 상수), 메소드(함수)로 구성
생성 방법struct 이름 {} 형태로 정의
초기화init() 자동 제공 (Memberwise Initializer)
타입값 타입 (Value Type) → 복사되어 전달
메소드에서 프로퍼티 변경mutating 키워드 필요
상속불가능
let으로 생성 시 내부 프로퍼티 변경불가능

예시

struct Person {
    var name: String
    var age: Int = 0 // 기본값

    func introduce() {
        print("나는 \(name)이고 \(age)살 이당")
    }

    mutating func happyNewYear() {
        age += 1
    }
}

let me = Person(name: "가현", age: 20)
me.introduce() // 출력: 나는 가현이고 20살 이당

struct 초기화

Memberwise Initializer (자동 제공)

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

let p = Person(name: "가현", age: 20)

기본값이 있으면 생략 가능

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

let p1 = Person(name: "가현")       // age는 기본값 사용
let p2 = Person(name: "가현", age: 20)

직접 init 정의하면 자동 init 제공 ㄴㄴ

struct Person {
    var name: String
    var age: Int

    init() {
        name = "이름 없음"
        age = 0
    }
}
let p = Person()                    // 사용 가능
let p2 = Person(name: "가현")     // 에러 발생

여러 개의 init 정의

struct Person {
    var name: String
    var age: Int

    init() {
        self.name = "이름 없음"
        self.age = 0
    }

    init(name: String) {
        self.name = name
        self.age = 0
    }

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

 VS

class vs struct 차이 정리

항목classstruct
타입참조 타입 (Reference Type)값 타입 (Value Type)
상속가능불가능
init 제공수동 작성 필요자동 init 제공 (Memberwise)
메모리같은 인스턴스를 참조복사되어 사용
메소드에서 프로퍼티 변경제한 없음mutating 키워드 필요
클래스 전용 기능상속, 타입 캐스팅, 디이니셜라이저, 참조 카운팅 등없음
사용 용도복잡한 객체 관리 (예: 뷰 컨트롤러 등)간단한 데이터 모델

Function vs Method

항목FunctionMethod
정의 위치클래스, 구조체, 열거형 외부내부
호출 방식함수 이름만으로 호출인스턴스를 통해 호출 (object.method())

ex)

func sayHello() { print("Hello") } // Function

struct Person {
    func introduce() { print("안녕") } // Method
}

Xcode 팁
Function → f 아이콘으로 표시
Method → M 아이콘으로 표시

프로퍼티 vs 변수·상수

항목프로퍼티변수/상수
정의 위치struct, class, enum 내부외부
역할객체의 상태(값)를 저장값 저장 (일반적인 변수)

ex)

struct Person {
    var name: String // 프로퍼티
}

let person = Person(name: "가현") // 변수(상수)

Xcode 팁
Property → P 아이콘
일반 변수/상수 → V 아이콘

프로퍼티 정리

유형설명특징사용 가능 위치
저장 프로퍼티값을 저장하는 기본 프로퍼티var, let 사용 가능class, struct (enum X)
연산 프로퍼티계산된 값을 반환, 저장 Xvar만 가능, get, set 사용class, struct, enum
타입 프로퍼티타입에 귀속된 프로퍼티static 사용 (class에서는 class 가능)class, struct, enum
프로퍼티 옵저버값 변경 감지 (willSet, didSet)저장 프로퍼티에만 가능, 이름 지정 가능class, struct
지연 저장 프로퍼티처음 접근 시 초기화lazy var, 초기화 비용이 큰 경우 사용class, struct (let X, enum X)

메소드 정리 요약

유형설명주의사항
인스턴스 메소드인스턴스를 통해 호출되는 일반 메소드class는 프로퍼티 자유롭게 변경 가능 struct, enum은 mutating 키워드 필요
타입 메소드타입에 직접 호출하는 메소드static func, class func 사용 (상속 허용시 class)

언제 lazy 씀???

API 통신 후 데이터를 넣는 변수
무거운 계산이 필요한 값 (예: 큰 배열)
처음부터 꼭 필요한 값이 아닌 경우

연산 프로퍼티 vs 메소드

단순히 값을 계산해서 제공: 연산 프로퍼티
값을 외부에 노출하지 않고 로직을 실행: 메소드

// 연산 프로퍼티
var bmi: Double {
    return weight / (height * height)
}

// 메소드
func calculateBMI() -> Double {
    return weight / (height * height)
}

willSet, didSet은 언제??????

값이 변경되기 전/후 동작을 감지하고 싶을 때
ex ) 사용자 이름 바뀔 때 알림 보내기


playground로 계산기 앱? 구축하기

import UIKit

enum CalculatorError: Error, CustomStringConvertible {
    case divideByZero
    case invalidOperator
    
    var description: String {
        switch self {
        case .divideByZero:
            return "0으로는 나눌 수 없음"
        case .invalidOperator:
            return "연산자 안됨"
        }
    }
}

class Calculator {
    func calculate(operator op: String, firstNumber: Double, secondNumber: Double) throws -> Double {
        switch op {
        case "+":
            return firstNumber + secondNumber
        case "-":
            return firstNumber - secondNumber
        case "*":
            return firstNumber * secondNumber
        case "/":
            if secondNumber == 0 {
                throw CalculatorError.divideByZero
            }
            return firstNumber / secondNumber
        default:
            throw CalculatorError.invalidOperator
        }
    }
}

let calculator = Calculator()

do {
    let sum = try calculator.calculate(operator: "+", firstNumber: 10, secondNumber: 5)
    print("덧셈 : \(sum)")
    
    let div = try calculator.calculate(operator: "/", firstNumber: 10, secondNumber: 0)
    print("나눗셈 : \(div)")
} catch let error as CalculatorError {
    print(error.description, terminator: "")
}

1단계 완료.
여기까지는 그닥 어려운 게 없었음
playground로 뭘 만드려는 게 첨이라 신기했당

0개의 댓글