본문 바로가기
iOS프로그래밍 실무

[iOS프로그래밍 실무] 7주차

by heeaeeeee 2025. 4. 17.

열거형 UITableViewCell.CellStyle

enum을 사용하는 프로그래밍 언어

언어 특성 예시
Swift 연관값, 메서드 포함 가능 enum Direction { case north }
C/C++  정수 기반, 기본적 enum Color { RED, GREEN }
Java  클래스형 enum, 필드/메서드 포함 가능  enum Day { MONDAY, TUESDAY }
Python  enum 모듈 이용 class Direction(Enum): ...
Kotlin  강력한 enum, 메서드 포함 가능 enum class Direction { ... }

 

열거형 정의

enum Compass {
    case North
    case South
    case East
    case West
}
//var x : Compass // Compass형 인스턴스 x
print(Compass.North) // North
var x = Compass.West
print(type(of:x)) // Compass
x = .East
print(x) // East

 

열거형 멤버별 기능 정의

enum Compass {
    case North
    case South
    case East
    case West
}
var direction : Compass
direction = .South
switch direction { //switch의 비교값이 열거형 Compass
case .North: //direction이 .North이면 "북" 출력
    print("북")
case .South:
    print("남")
case .East:
    print("동")
case .West:
    print("서") //모든 열거형 case를 포함하면 default 없어도 됨
}

 

열거형 멤버에는 메서드도 가능

enum Week {
    case Mon,Tue,Wed,Thur,Fri,Sat,Sun
    func printWeek() { //메서드도 가능
        switch self {
        case .Mon, .Tue, .Wed, .Thur, .Fri:
            print("주중")
        case .Sat, .Sun:
            print("주말") //출력결과 : 주말
        }
    }
}
Week.Sun.printWeek() //??

 

열거형의 rawValue

enum Color: Int { //원시값(rawValue) 지정
    case red
    case green = 2
    case blue
}
print(Color.red) //red
print(Color.blue) //blue
print(Color.red.rawValue) //0
print(Color.blue.rawValue) //3

 

String형 값을 갖는 열거형의 rawValue

enum Week: String {
    case Monday = "월"
    case Tuesday = "화"
    case Wednesday = "수"
    case Thursday = "목"
    case Friday = "금"
    case Saturday //값이 지정되지 않으면 case 이름이 할당됨
    case Sunday // = "Sunday"
}
print(Week.Monday) //Monday
print(Week.Monday.rawValue) //월
print(Week.Sunday) //Sunday
print(Week.Sunday.rawValue) //Sunday

 

연관 값(associated value)을 갖는 enum

enum Date {
    case intDate(Int,Int,Int) //(int,Int,Int)형 연관값을 갖는 intDate
    case stringDate(String) //String형 연관값을 값는 stringDate
}
var todayDate = Date.intDate(2025,4,30)
todayDate = Date.stringDate("2025년 5월 20일") //주석처리하면? 2025년 4월 30일
switch todayDate {
case .intDate(let year, let month, let day):
    print("\(year)년 \(month)월 \(day)일")
case .stringDate(let date):
    print(date) //2025년 5월 20일
}

 

옵셔널은 연관 값(associated value)을 갖는 enum

let age: Int? = 30 //Optional(30)
switch age {
case .none: // nil인 경우
    print("나이 정보가 없습니다.")
case .some(let a) where a < 20:
    print("\(a)살 미성년자입니다")
case .some(let a) where a < 71:
    print("\(a)살 성인입니다")
default:
    print("경로우대입니다")
} //30살 성인입니다.
var x : Int? = 20 //.some(20)
var y : Int? = Optional.some(10)
var z : Int? = Optional.none
var x1 : Optional<Int> = 30
print(x, y, z, x1) //Optional(20) Optional(10) nil Optional(30)

 

iOS 개발할 때 열거형 사용

네트워크 요청 상태 관리용 열거형

enum NetworkState {
    case idle            // 요청 대기 상태
    case loading         // 요청 중
    case success(data: Data)  // 요청 성공, 연관값으로 데이터 포함
    case failure(error: Error) // 요청 실패, 연관값으로 에러 포함
}

사용 예시

func handleNetworkState(_ state: NetworkState) {
    switch state {
    case .idle:
        print("대기 상태입니다.")
    case .loading:
        print("로딩 중입니다.")
    case .success(let data):
        print("성공! 데이터 크기: \(data.count) 바이트")
    case .failure(let error):
        print("실패: \(error.localizedDescription)")
    }
}

 

  • NetworkState 열거형은 네트워크 요청의 여러 상태를 표현
  • .success와 .failure 케이스는 **연관값(Associated Values)**을 가지고 있어, 성공 시 데이터를, 실패 시 에러 정보를 함께 저장할 수 있음
  • switch 문을 사용해 상태별로 적절한 처리를 할 수 있음
  • 이렇게 하면 상태 관리가 명확해지고, 코드도 안전하고 가독성이 좋아짐

 

구조체

 

swift에서 구조체를 사용할 때와 클래스를 사용할 때

1. 구조체를 사용하는 경우

  • 값 타입(Value Type): 구조체는 값을 복사해서 전달합니다. 따라서 복사본이 독립적이며, 원본에 영향을 주지 않음
  • 불변성 유지가 중요할 때: 값이 변경되면 안 되거나, 상태가 독립적으로 유지되어야 할 때 적합함
  • 상태가 가볍고, 단순한 데이터 모델: 좌표, 크기, 색상, 설정값 등 간단한 데이터 표현에 적합함
  • 상속이 필요 없을 때: 구조체는 상속을 지원하지 않음
  • 스레드 안전성: 값 복사 방식이라 멀티스레드 환경에서 안전함
struct Point {
    var x: Int
    var y: Int
}

var p1 = Point(x: 0, y: 0)
var p2 = p1
p2.x = 10
print(p1.x) // 0, p2.x는 10으로 독립적임

 

2. 클래스를 사용하는 경우

  • 참조 타입(Reference Type): 클래스는 참조를 전달합니다. 즉, 여러 변수가 같은 인스턴스를 가리킬 수 있음
  • 상태 공유가 필요할 때: 객체를 여러 곳에서 공유하며 상태가 변하면 모두에게 영향을 줘야 할 때 적합함
  • 상속이 필요할 때: 클래스는 상속과 다형성(polymorphism)을 지원함
  • 메모리 관리: ARC(자동 참조 카운트)를 통해 메모리를 관리함
  • 복잡한 객체 관리: UI 컴포넌트, 네트워크 매니저 등 복잡한 동작과 상태를 가진 객체에 적합함
class Person {
    var name: String
    init(name: String) {
        self.name = name
    }
}

var person1 = Person(name: "Alice")
var person2 = person1
person2.name = "Bob"
print(person1.name) // Bob, 참조가 공유됨

 

요약

특징 구조체 (Struct) 클래스 (Class)
타입 값 타입 참조 타입
복사 방식  값 복사, 독립적 참조 전달, 공유
상속 불가 가능
사용 용도  간단한 데이터 모델, 불변성 유지 복잡한 상태 관리, 상속 필요시
멀티스레드 안전성 상대적으로 안전 주의 필요

 

클래스 내에 구조체

struct Resolution {
    var width = 1024
    var height = 768
}
class VideoMode {
    var resolution = Resolution()
    var frameRate = 0.0
}
let myVideo = VideoMode()
print(myVideo.resolution.width) //1024

 

class가 struct보다 더 갖는 특징

클래스/구조체 정의하기

 

구조체는 값 타입(value type) 클래스는 참조 타입(reference type)

struct Human {
    var age : Int = 1
}
var kim = Human()
var lee = kim //값 타입
print(kim.age, lee.age)
lee.age = 20
print(kim.age, lee.age)
kim.age = 30
print(kim.age, lee.age)
var x = 1
var y = x
print(x,y)
x = 2
print(x,y)
y = 3
print(x,y)
//값 타입은 복사할 때 새로운 데이터가 하나 더 생김
class Human {
    var age : Int = 1
}
var kim = Human()
var lee = kim //참조 타입
print(kim.age, lee.age)
lee.age = 20
print(kim.age, lee.age)
kim.age = 30
print(kim.age, lee.age)
//참조 타입은 복사할 때 주소를 복사해서
//한 데이터의 reference가 2개 생김

 

프로그래밍 언어에서 value type과 reference type을 비교

1. 값 타입(value type)

  • 특징: 변수를 할당하거나 함수에 전달할 때, 실제 데이터가 복사되어 전달
  • 복사본이 독립적이기 때문에 한 변수의 값을 변경해도 다른 변수에 영향을 미치지 않음
  • 주로 기본 데이터 타입(숫자, 불리언, 구조체 등)이 값 타입으로 구현
struct Point {
    var x: Int
    var y: Int
}

var p1 = Point(x: 0, y: 0)
var p2 = p1       // p1이 p2에 복사됨 (값 복사)
p2.x = 10

print(p1.x) // 0, p1은 변하지 않음
print(p2.x) // 10, p2만 변경됨

 

2. 참조 타입 (Reference Type)

  • 특징: 변수를 할당하거나 함수에 전달할 때, 데이터가 저장된 메모리 주소(참조)가 전달
  • 여러 변수가 같은 객체를 참조하기 때문에, 한 변수를 통해 객체를 변경하면 모든 참조가 영향을 받음
  • 주로 클래스 객체가 참조 타입
class Person {
    var name: String
    init(name: String) {
        self.name = name
    }
}

var person1 = Person(name: "Alice")
var person2 = person1  // person1과 person2가 같은 객체를 참조

person2.name = "Bob"

print(person1.name) // Bob, person1도 변경된 값 반영
print(person2.name) // Bob

차이점 요약

구분  값 타입(Value Type)  참조 타입(Reference Type)
복사 방식  데이터 자체 복사  데이터 주소(참조) 복사
변수 간 독립성  독립적 (변경 시 서로 영향 없음)  공유 (변경 시 모두 영향 받음)
메모리 위치  스택(stack)에 저장되는 경우 많음  힙(heap)에 저장되고 참조 전달
대표 타입  Int, Float, Bool, Struct, Enum  Class, Closure

언제 어떤 타입을 써야 할까?

  • 값 타입은 데이터가 작고 단순하며, 복사해도 문제가 없을 때 적합
  • 참조 타입은 여러 곳에서 같은 객체를 공유하고 상태를 관리해야 할 때 적합

 

class Human {
    var age: Int = 1 // Human 클래스의 프로퍼티 age를 초기값 1로 선언
}

var kim = Human()    // Human 인스턴스 하나 생성, kim이 참조(주소)를 가짐
var lee = kim        // lee도 kim이 가리키는 같은 인스턴스(참조)를 가리킴

print(kim.age, lee.age) // 출력: 1 1
// kim과 lee가 같은 인스턴스를 참조하므로 age 값이 같음

lee.age = 20
// lee를 통해 인스턴스의 age 값을 20으로 변경
// kim도 같은 인스턴스를 참조하므로 kim.age도 20으로 바뀜

print(kim.age, lee.age) // 출력: 20 20
// 두 변수 모두 같은 인스턴스를 가리키기 때문에 변경 사항이 반영됨

kim.age = 30
// 이번엔 kim을 통해 age 값을 30으로 변경

print(kim.age, lee.age) // 출력: 30 30
// 역시 같은 인스턴스를 참조하므로 lee.age도 30으로 변경됨

// 정리: 참조 타입은 변수에 할당할 때 '인스턴스의 주소(참조)'를 복사함
// 따라서 kim과 lee는 같은 인스턴스를 가리키는 '참조' 2개가 존재
// 한쪽에서 값을 변경하면, 다른 쪽에서도 변경된 값이 보임

 

구조체는 값 타입(value type) 클래스는 참조 타입(reference type)

struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()
    var frameRate = 0
    var name: String?
}
var hd = Resolution(width: 1920, height: 1080)
//자동 Memberwise Initializer
var highDef = hd
//구조체는 값타입(value type)
print(hd.width, highDef.width)
hd.width = 1024
print(hd.width, highDef.width)
var xMonitor = VideoMode()
xMonitor.resolution = hd
xMonitor.name = "LG"
xMonitor.frameRate = 30
print(xMonitor.frameRate)
var yMonitor = xMonitor
//클래스는 참조타입(reference type)
yMonitor.frameRate = 25
print(yMonitor.frameRate)
print(xMonitor.frameRate)

 

swift에서 class, struct, enum, protocol, extension 사용

개념 특징 언제 사용하는가? 예시 용도
Class 참조 타입, 상속 가능 상태 공유, 상속, 복잡한 객체 뷰 컨트롤러, 네트워크 매니저
Struct 값 타입, 상속 불가 가벼운 데이터, 불변성, 복사 필요 좌표, 색상, 설정값
Enum 상태 표현, 연관값 가능 명확한 상태, 옵션, 이벤트 관리 네트워크 상태, 앱 상태
Protocol 인터페이스 정의, 다형성 지원 공통 기능 강제, 타입 추상화 드라이브 가능한 객체, 데이터 소스
Extension  기존 타입 기능 확장  기존 타입에 기능 추가, 코드 분리 유틸리티 함수, 프로토콜 준수 분리


class, struct, enum, protocol, extension 모두 들어간 iOS 소스

import Foundation

// MARK: - Enum (사용자 상태)
enum UserStatus {
    case active
    case inactive
    case banned(reason: String) // 연관값 포함
    
    func description() -> String {
        switch self {
        case .active:
            return "활성 상태"
        case .inactive:
            return "비활성 상태"
        case .banned(let reason):
            return "정지됨: \(reason)"
        }
    }
}

// MARK: - Protocol (사용자 정보 표시)
protocol UserDisplayable {
    func displayInfo() -> String
}

// MARK: - Struct (주소 정보, 값 타입)
struct Address {
    var city: String
    var street: String
}

// MARK: - Class (사용자 클래스, 참조 타입)
class User: UserDisplayable {
    var name: String
    var age: Int
    var status: UserStatus
    var address: Address
    
    init(name: String, age: Int, status: UserStatus, address: Address) {
        self.name = name
        self.age = age
        self.status = status
        self.address = address
    }
    
    func displayInfo() -> String {
        return "이름: \(name), 나이: \(age), 상태: \(status.description()), 주소: \(address.city) \(address.street)"
    }
}

// MARK: - Extension (User 클래스에 기능 추가)
extension User {
    func isAdult() -> Bool {
        return age >= 18
    }
    
    func updateAddress(city: String, street: String) {
        address = Address(city: city, street: street)
    }
}

// MARK: - 사용 예시

let address = Address(city: "Seoul", street: "Gangnam-daero 123")
let user = User(name: "홍길동", age: 25, status: .active, address: address)

print(user.displayInfo()) // 이름: 홍길동, 나이: 25, 상태: 활성 상태, 주소: Seoul Gangnam-daero 123
print("성인 여부: \(user.isAdult() ? "성인" : "미성년자")") // 성인 여부: 성인

user.updateAddress(city: "Busan", street: "Haeundae-ro 456")
print(user.displayInfo()) // 변경된 주소 반영 출력

 

 

 

iOS 강의 자료 참고했습니다