정해진 값들 중에서 하나를 고를 수 있게 그룹으로 묶어놓은 것
예를들어 ) 봄, 여름, 가을, 겨울
👉🏻 정해진 선택지들끼리 하나의 상자에 묶어놓은 것
ex) 어떤 앱을 만들고 있는데 사용자의 계절을 선택해야 한다고 가정했을 때
그냥 문자열로 spring, summer 이렇게 쓰면 오타가 날 수도 있고, 없는 계절을 넣을 수도 있음
👉🏻 열거형을 쓰면 정해진 값만 쓸 수 있게 해서 더 안전하고 실수도 줄일 수 있음
var currentSeason = Season.spring
이렇게 하면 현재 계절이 봄이라고 저장됨
Season.spring처럼 열거형이름.값이름 형식으로 쓰는데,
앞에 Season을 생략하고 .spring만 써도 괜찮을 때도 있음 (이미 어떤 열거형인지 컴퓨터가 알 때)
ex) 택배 상태를 나타내는 열거형이 있다고 할 때
그중 <배송중>에는 송장번호가 필요할 수도 있을텐데
그럴 때 <배송중>이라는 상태에 송장번호를 함께 넣을 수 있음
👉🏻 어떤 값을 고르면서 거기에 정보를 하나 더 붙여서 저장할 수 있는 것
예를 들어 월을 열거형으로 만들면
1월 = 1, 2월 = 2 이런 식으로 숫자를 붙이고 싶을 수 있음 근데 붙이나? 암튼 있다고 했을 때
👉🏻 그럴 땐 열거형의 각 값에 숫자나 문자열 같은 기본값을 줄 수 있음
자동으로 붙기도 하고, 직접 정해줄 수도 있음
ex) 봄이면 따뜻함, 겨울이면 추움 이런 말
👉🏻 그럴 땐 열거형 안에 함수를 만들어서 상황에 따라 다른 말을 하게 할 수 있음
즉, 그냥 선택지만 있는 게 아니라 각 선택지가 자기 역할도 하게 만드는 것인 셈
인스턴스 : 진짜로 만들어진 것(?)
음.. 예를들어 설계도만 있다고 해서 자동차가 당장 막 굴러 갈 수 있는 건 아님.
설계도를 보고 진짜 자동차를 뚝딱뚝딱 만드는 게 인스턴스임
마찬가지로 열거형도 설계도고,
거기서 .spring 같은 실제 값을 만들어서 쓰는 게 인스턴스
| 개념 | 쉽게 말해 |
|---|---|
| enum | 정해진 선택지들을 그룹으로 묶은 것 |
| case | 그 그룹 안의 하나하나의 선택지 |
| 연관값 | 선택지에 추가로 붙는 정보 |
| 원시값 | 선택지에 기본적으로 지정되는 값 (숫자나 글자 등) |
| 함수 | 선택지가 자기가 할 말을 정해서 말하는 기능 |
| 인스턴스 | 설계도(enum)를 가지고 만든 진짜 값 |
클래스는 설계도.
무언가를 만들기 위한 설계도처럼, 클래스는 이런 정보를 가지고 이런 행동을 해야함. !!! 라고 미리 정해놓는 거
예를들어 ) 학생을 생각해봤을 때
학생은 이름이 있고 나이가 있음.
공부를 하거나, 시험을 볼 수도 있음
뭐 이런 여러 가지 것들을 코드로 만들기 위해 클래스가 필요한 것임. (분할??의 개념인가)
→ 정보 저장 (ex. 이름, 나이)
→ 어떤 행동 (ex. 공부하기, 시험보기)
클래스 정의 (설계도 만들기)
인스턴스 만들기 (설계도를 보고 학생 하나 만들기)
만든 학생한테 이름도 넣고, 행동도 시켜볼 수 있음
클래스를 만들 땐, 이 학생은 이름이 뭔지, 몇 살인지 알려줘야 함
그래서 처음 만들 때 초기화(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 | 같은 붕어빵을 나눠 먹는 느낌 (같은 걸 가리킴) |
| 상속 | 붕어빵 틀을 물려받아서 새로운 틀 만들기 |
구조체는 물건 박스
어떤 정보를 담고 싶을 때 쓰는 박스 같은 거
예를들어 ) 사람이라는 정보를 담는 박스를 만들 수 있음
→ 정보를 담는 변수들 (예: 이름, 나이)
→ 구조체가 할 수 있는 행동 (예: 자기소개하기)
| 구분 | 설명 |
|---|---|
| 프로퍼티 & 메소드 | 정보를 저장하고, 행동을 정의할 수 있음 |
| 인스턴스 생성 가능 | 구조체() 로 실제로 사용할 수 있음 |
| 기본값 설정 가능 | 변수에 기본값 줄 수 있음 |
| 멤버와이즈 이니셜라이저 제공 | 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로 증가
struct Person {
var name: String
var age: Int
}
let brody = Person(name: "가현", age: 10)
// init 만들지 않아도 자동으로 name과 age를 넣는 생성자 만들어줌
struct Person {
var name: String
var age: Int = 0 // 기본값
func introduce() {
print("나는 \(name)이고 저는 \(age)살 이당")
}
}
let a = Person(name: "가현")
// 출력: 나는 가현이고 0살 이당
struct Person {
var name: String
var age: Int
init() {
self.name = "이름없음"
self.age = 0
}
}
직접 init()을 만들면 자동 생성되는 멤버와이즈 init은 사라짐
구조체는 값 타입이라 복사하면 서로 독립적인 다른 값임 !!!!!!!!!!!!!!
→ A를 복사해서 만든 B는, A랑 아무 관련이 없음
구조체 안에서 프로퍼티를 바꾸는 함수는 반드시 mutating 키워드를 붙여야 함
| 항목 | 클래스 | 구조체 |
|---|---|---|
| 타입 | 참조 타입 | 값 타입 |
| 복사 시 | 같은 걸 가리킴 (공유됨) | 새로 복사됨 (독립됨) |
| 상속 | 가능 | 불가능 |
| init 자동 생성 | 없음 | 있음 (멤버와이즈 init) |
| mutating 키워드 | 필요 없음 | 필요함 (프로퍼티 수정 시) |
| deinit | 있음 | 없음 |
데이터와 행동을 묶은 사용자 정의 데이터 타입
| 항목 | 설명 |
|---|---|
| 구성 요소 | 프로퍼티(변수, 상수), 메소드(함수)로 구성 |
| 생성 방법 | 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 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)
struct Person {
var name: String
var age: Int
init() {
name = "이름 없음"
age = 0
}
}
let p = Person() // 사용 가능
let p2 = Person(name: "가현") // 에러 발생
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
}
}
| 항목 | class | struct |
|---|---|---|
| 타입 | 참조 타입 (Reference Type) | 값 타입 (Value Type) |
| 상속 | 가능 | 불가능 |
| init 제공 | 수동 작성 필요 | 자동 init 제공 (Memberwise) |
| 메모리 | 같은 인스턴스를 참조 | 복사되어 사용 |
| 메소드에서 프로퍼티 변경 | 제한 없음 | mutating 키워드 필요 |
| 클래스 전용 기능 | 상속, 타입 캐스팅, 디이니셜라이저, 참조 카운팅 등 | 없음 |
| 사용 용도 | 복잡한 객체 관리 (예: 뷰 컨트롤러 등) | 간단한 데이터 모델 |
| 항목 | Function | Method |
|---|---|---|
| 정의 위치 | 클래스, 구조체, 열거형 외부 | 내부 |
| 호출 방식 | 함수 이름만으로 호출 | 인스턴스를 통해 호출 (object.method()) |
ex)
func sayHello() { print("Hello") } // Function
struct Person {
func introduce() { print("안녕") } // Method
}
Xcode 팁
Function → f 아이콘으로 표시
Method → M 아이콘으로 표시
| 항목 | 프로퍼티 | 변수/상수 |
|---|---|---|
| 정의 위치 | struct, class, enum 내부 | 외부 |
| 역할 | 객체의 상태(값)를 저장 | 값 저장 (일반적인 변수) |
ex)
struct Person {
var name: String // 프로퍼티
}
let person = Person(name: "가현") // 변수(상수)
Xcode 팁
Property → P 아이콘
일반 변수/상수 → V 아이콘
| 유형 | 설명 | 특징 | 사용 가능 위치 |
|---|---|---|---|
| 저장 프로퍼티 | 값을 저장하는 기본 프로퍼티 | var, let 사용 가능 | class, struct (enum X) |
| 연산 프로퍼티 | 계산된 값을 반환, 저장 X | var만 가능, 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) |
API 통신 후 데이터를 넣는 변수
무거운 계산이 필요한 값 (예: 큰 배열)
처음부터 꼭 필요한 값이 아닌 경우
단순히 값을 계산해서 제공: 연산 프로퍼티
값을 외부에 노출하지 않고 로직을 실행: 메소드
// 연산 프로퍼티
var bmi: Double {
return weight / (height * height)
}
// 메소드
func calculateBMI() -> Double {
return weight / (height * height)
}
값이 변경되기 전/후 동작을 감지하고 싶을 때
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로 뭘 만드려는 게 첨이라 신기했당