Post

Swift 프로그래밍 4판(야곰) 정리

Swift 프로그래밍 4판(야곰) 정리

스위프트 기초

스위프트 언어의 특성

  • 안전성: 개발자의 실수를 강제적이라고 느낄 수 있는 문법적 제재(옵셔널, guard 구문 등)를 통해 제어.
  • 객체지향 프로그래밍: 여러 개의 독립된 단위인 객체의 모임으로 파악. 객체는 서로 메시지를 주고받으며 데이터 처리

객체지향에서의 클래스와 객체

클래스:

  • 같은 종류의 집간에 속하는 속성과 행위를 정의한 것.
  • 사용자 정의 데이터 타입

객체:

  • 클래스의 인스턴스(실제로 메모리에 할당되어 동작하는 것).
  • 객체는 자신 고유의 속성과 메소드가 있음.

메서드:

  • 객체가 클래스에 정의된 행위를 실질적으로 동작하는 함수.

함수형 프로그래밍

특징

  • 함수형 프로그래밍은 순수하게 함수에 전달된 인자 값만 결과에 영향을 주므로 상태 값을 갖지 않고 순수하게 함수만으로 동작. 따라서 병렬처리가 가능
  • 함수형 프로그래밍은 함수를 일급 객체로 다름
  • 일급 객체 조건:
    1. 함수 자체를 전달인자로 전달할 수 있음
    2. 동적 프로퍼티 할당이 가능 -> 함수를 변수에 할당할 수 있다는 뜻
    3. 변수나 데이터 구조 안에 담을 수 있음 -> 함수를 다른 값들처럼 배열, 딕셔너리, 구조체 안에 저장 가능
    4. 반환 값으로 사용 가능
    5. 할당할 때 사용된 이름과 관계없이 고유한 객체로 구별할 수 있음 -> 같은 기능을 해도 서로 다른 함수는 고유함 ``` let a = { print(“hi”) } let b = { print(“hi”) } print(a == b) // 에러: Swift에서는 클로저끼리 == 비교 불가, 즉 서로 다른 인스턴스임

    ```

    프로토콜 지향

    스위프트는 구조체와 열거형에 기존의 클래스에서 구현할 수 있었던 캡슐화, 추상화, 접근 제어 등의 기능을 모두 구현 가능 더불어 프로토콜 익스텐선을 활용할 수 있기 때문에 프로토콜 지향 프로그래밍이 가능


스위프트 시작하기

변수와 상수

변수와 상수를 이용해 필요한 데이터들을 메모리에 임시로 저장 변수: 생성 후 데이터 변경 가능 - var로 선언 상수: 생성 후 데이터 변경 불가 - let으로 선언

1
변수 생성시 타입을 생략하면 컴파일러가 타입을 추론함


데이터 타입

Int와 UInt

Int: +, - 를 포함한 정수 UInt: 0을 포함한 양의 정수

  • 각각 8, 16, 32, 64비트의 형태가 있음 ( 예) Int8, UInt8 )
  • 시스템 아키텍처에 따라 32비트에서는 Int32, UInt32가 Int, UInt 타입으로 지정되고 64비트도 마찬가지
  • 정수 범위: Int.min < UInt.min(== 0) < Int.max < UInt.max

Bool

참(true), 거짓(false)만 가지는 타입

1
2
3
4
var boolean: Bool = false
print(boolean) // false
boolean.toggle()
print(boolean) // true

Float, Double

부동소수점을 사용하는 실수, 부동소수 타입 Float: 32비트로 표현 -> 최소 6자리의 숫자까지 표현 가능 Double: 62비트로 표현 -> 최소 15자리의 숫자까지 표현 가능

  • 임의의 수 만들기
    1
    2
    3
    4
    
    Int.random(in: -100...100)
    UInt.random(in: 1...30)
    Double.random(in: 1.5...4.3)
    Float.random(in: -0.5...1.5)
    

Character

단 하나의 문자 유니코드, 한글 등으로 사용 가능

1
let cha: Character = "A"

String

문자의 나열. 즉 문자열

1
2
3
4
let name: String = "Cha Sangjin"
let greeting = """
안녕하세요. 저는 차상진입니다.
"""

Any, AnyObject와 nil

Any: 스위프트의 모든 데이터 타입을 사용할 수 있다는 뜻

1
2
3
4
5
6
var value: Any

value = 123
value = "Swift 최고!"
value = true

AnyObject: Any보다는 조금 한정된 의미로 클래스의 인스턴스만 할당 가능 구조체(struct)나 열거형(enum)은 담을 수 없고, 오직 클래스(class) 인스턴스만 가능

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Car {
    var brand: String
    init(brand: String) {
        self.brand = brand
    }
}

class Book {
    var title: String
    init(title: String) {
        self.title = title
    }
}

let items: [AnyObject] = [
    Car(brand: "Tesla"),
    Book(title: "Swift의 정석")
]

for item in items {
    if let car = item as? Car {
        print("자동차 브랜드: \(car.brand)")
    } else if let book = item as? Book {
        print("책 제목: \(book.title)")
    }
}


데이터 타입 심화

  • 서로 다른 타입끼리 데이터 교환시 타입캐스팅(형변환)을 거쳐야 함

타입 별칭 추가

1
2
3
4
5
6
7
typealias MyInt = Int
typealias YourInt = Int

let age: MyInt = 100
let year: YoutInt = 2025

age = year // 같은 Int라서 할당 가능

튜플

  • 지정된 데이터의 묶음
  1. 인덱스로 접근 가능
    1
    2
    
    var person: (String, Int, Double) = ("sangjin", 27, 182.4)
    print("이름: \(person.0)")
    
  2. 이릉으로 접근 가능
    1
    2
    
    var person: (name: String, age: Int, height: Double) = ("sangjin", 27, 182.4)
    print("이름: \(person.name)")
    
  3. 별칭 지정 가능 ``` typealias PersonTuple = (name: String, age: Int, height: Double)

let sangjin: PersonTuple = (“sangjin”, 27, 183.2)

1
2
3
4
5
6
<br>

## 컬렉션형
### 배열 
- 같은 타입의 데이터를 일렬로 나열

// 동일한 표현 var nums: Array = [0, 1, 2] var nums: [Int] = [0, 1, 2]

1
2
3
4
### 딕셔너리
- 순서없이 키와 값이 쌍으로 구성되는 컬렉션 타입
- 키는 유일해야 함

var numberForname: [String:Int] = [“sangjin” : 10, “Haewon” : 20] print(numberForName[“sangjin”]) // 10 출력 numberForName[“sangjin”] = 15 // 15 할당 numberForName[“winter”] = 25 // 새 딕셔너리 키 생성과 값 할당

1
2
3
4
5
6
7
8



### 세트
- 데이터들을 순서없이 중복없이 하나의 묶음으로 저장하는 형태의 컬렉션 타입
- 세트의 요소는 해시 가능한 값이어야 함
- Array와 마찬가지로 대괄호를 사용하기때문에 타입 추론시 Array로 추론함

var names: Set = [] names = ["sangjin", "winter", "haewon"]

1
2
`insert`로 삽입

names.insert(“karina”)

1
2
`remove`로 제거

names.remove(“sangjin”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- 또한 세트 내부의 값들은 유일함을 보장하기 때문에 교집합, 여집합 등 여러 집합연산이 필요할 때 유용하다.


### 열거형
- 연관된 항목들을 묶어서 표현
- 정의시 외에 추가/수정 불가
- 원시 값(raw value)형태로 실제 값을 가질 수도 있음

1. 열거형 정의
   아래의 두가지 방식으로 정의 가능
    ```
    enum School {
      case middle
      case high
      case college
    }

    enum School {
      case middle, high, college
    }
    ```
  
3. 열겨형 변수 생성
   아래의 두가지 방식으로 정의 가능

var school: School = School.middle var school = .middle

1
2
  
5. 열거형 원시 값 할당
1
2
3
4
5
enum School: String {
  case middle = "중학교"
  case high = "고등학교"
  case college = "대학교"
}

let school: School = .middle print(school.rawValue) // 중학교

1
2
3
6. 원시 값 일부만 할당
- Swift의 enum에서 rawValue를 명시하지 않으면, 숫자형 타입에서는 이전 값보다 1 증가된 숫자가 자동 할당되고, 문자열 타입에서는 case 이름이 자동으로 원시값으로 사용

enum School: String { case middle = “중학교” case high = “고등학교” case college } let school: School = .college print(school.rawValue) // college

1

enum Numbers: Int { case a // 0 case b // 1 case c = 10 case d // 11 ← 자동 증가 }

let num: Numbers = .d print(num) // 11

1
2
3
4
7. 연관 값을 갖는 열겨형
- 열거형 내의 항목이 자신과 연관된 값을 가질 수 있음
- 항목별로 다른 값, 다른 타입을 가질 수 있음

// 정의 enum MainDish { case pasta(taste: String) case pizza(dough: String, topping: String) case chicken }

var dinner: MainDish = .pasta(taste: “크림”) dinner = .pizza(dough: “치즈 크러스트”, topping: “불고기”)

// 사용 switch dinner { case pasta(let taste): print(taste) // 크림 case pizza(dough, topping): // let 생략 가능 print(“(dough), (topping)”) // 치즈 크러스트, 불고기 case chicken: print(“치킨”) }

1
2
3
4

8. 항목 순회
- 열거형의 항목들을 순회가능하도록 만들려면 `CaseIterable`프로토콜을 채택해야 함

enum School: CaseIterable { case primary case elementary case middle case high case cellege }

let allCases: [School] = School.allCases print(allCases) // [primary, elementary, …]

1
2
3
- 하지만 연관값이 있을 경우에는 모든 값 조합을 컴파일러가 알 수 없기 때문에 allCases 프로퍼티를 사용할 수 없음
- 이럴 경우 직접 프로퍼티를 정의하여 사용

enum PastaTaste: CaseIterable { case Cream, tomato }

enum PizzaDough: CaseIterable { case cheeseCrust, thin, original }

enum PizzaTopping: CaseIterable { case pepperoni, cheese, bacon }

enum MainDish: CaseIterable { case pasta(taste: PastaTaste) case pizza(dough: PizzaDough, topping: PizzaTopping) case chicken(withSauce: Bool) case rice }

var allCases: [MainDish] { return PastaTaste.allCases.map(MainDish.pasta) + PizzaDough.allCases.reduce([]) { (result, dough) -> [MainDish] in result + PizzaTopping.allCases.map { (topping) -> MainDish in MainDish.pizza(dough: dough, topping: topping) } } }

1
2
3
4
9. 순환 열겨형
- 열겨형 항목의 관련 값이 자신일 때 사용
- 순환 열거형을 특정항목만 한정한다면 항목 앞에, 전체에 적용할 경우 enum 키워드 앞에 `indirect`키워드 사용

// 정의 에제 enum ArithmeticExpression { case number(Int) indirect case addition(ArithmeticExpression, ArithmeticExpression) indirect case multiplication(ArithmeticExpression, ArithmeticExpression) }

// 사용 예제 let five = ArithmeticExpression.number(5) // 5 let four = ArithmeticExpression.number(4) // 4 let sum = ArithmeticExpression.addition(five, four) // 9 let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2)) // 18

1
2
3
4
5

10. 비교 가능한 열거형
- `Comparable` 프로토콜을 준수하면 각 항목을 비교할 수 있음
- 작은 순서대로 작성

enum Condition: Comparable { case bad case good case great }

if Condition.bad < Condition.great { print(“Great”) } else { print(“Bad”) }

// Great 출력

1
2
3
4
5
6
<br>

# 연산자

### 삼항 연산자

var boolValue = false var result = boolValue ? “참” : “거짓” // 거짓 출력

boolValue = true result = boolValue ? “참” : “거짓” // 참 출력

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
### 범위 연산자
- 폐쇄 범위 연산자 : A부터 B까지의 수를 묶어 표현.(A, B 포함) `A...B`
- 반폐쇄 범위 연산자 : A부터 B미만까지 수를 묶어 표현 (A만 포함) `A..<B`
- 단방향 범위 연산자 :
  - A 이상의 수를 묶어 표현 (A포함) `A...`
  - A 이하의 수를 묶어 표현 (A포함) `...A`
  - A 미만의 수를 묶어 표현 (A 미포함) `..<A`
 
### 기타 연산자
- nil 병합 연산자 : A가 nil이면 B 반환, 아니면 A 반환 `A ?? B`
- 옵셔널 강제 추출 연산자 : `A!`
- 옵셔널 연산자 : 안전하게 추출하거나 A가 옵셔널임을 표현 `A?`


### 사용자 정의 연산자
- 연산자 종류:
  - 전위 연산자(`prefix`) : 피연산자 앞에 위치 `!A`
  - 중위 연산자(`infix`) : 피연산자들 사이에 위치 `A + B`
  - 후위 연산자(`postfix`) : 연산자 뒤에 위치 `A!`

- 연산자 키워드 : `operator`
- 전위 연산자 정의 예제

prefix operator ** // 새로 만들때는 정의하기 prefix func ** (value: Int) -> Int { return value * value }

let num = -5 let squareNum = **num print(squareNum) // 25

1
2
3
4
5
6
7
8
9
10
11
12
- 중위 연산자는 피연산자 사이에 위치하는 것이 명확하기 때문에 `func`앞에 키워드를 안붙여도 됨


<br>

# 흐름 제어

### if문
### switch문
- fallthrough 사용
case문이 실행되고 그 다음 case문이 실행되게 하기 위해서는 `fallthrough`를 사용하면 됨

let stringValue = “joker” switch stringValue { case “joker”: print(“joker”) case “sangjin” fallthrough }

1
2
3
- 튜플 사용
튜플로도 switch문 사용 가능

typealias NameAge = (name: String, age: Int) let tupleValue: NameAge = (“sangjin”, 21)

switch tupleValue { case (“sangjin”, 21): print(true) default: print(false) }

1
2
와일드 카드 식별자를 활용

typealias NameAge = (name: String, age: Int) let tupleValue: NameAge = (“sangjin”, 21)

switch tupleValue { case (“sangjin”, 21): print(true) case (_, 21): // 와일드 카드 사용 print(“나이만 맞음”) default: print(false) }

1
2
3

값 바인딩을 사용

typealias NameAge = (name: String, age: Int) let tupleValue: NameAge = (“sangjin”, 21)

switch tupleValue { case (“sangjin”, 21): print(true) case (let name, 21): // 바인딩 사용 print(“이름은 (name), 나이만 맞음.”) default: print(false) }

1
2
3
unknown 속성 사용
- default로 처리를 해놨는데 새로운 케이스가 추가되었을때 default로 처리되지 않고 추가한 case에 대한 경고를 표시해주기 위한 속성

enum Num { case 0, 1 }

let value = 0

switch value { case 0: print(0) @unknown default: // 1에 대한 처리를 하지 않아서 경고 발생 print(“다른 숫자”) }

1
2
3
4

### 표현으로서의 조건문
- 표현 형식을 사용하면 if 나 switch의 조건 결과를 변수나 상수에 바로 할당 가능

let someInt = 100 let size: String = if someInt > 10 { “큰 수” } else { “작은 수” } // 바로 할당

1

enum Menu { case chicken, pizza, hamburger }

let lunch = .pizza

let menu: String = switch lunch { case .pizza: “피자” default: “음식 없음” }

1
2
3
4
5
6
7
8
9
<br>

# 반복문

### for-in 반복문
배열, 튜플, 딕셔너리, set과 같은 자료형에서도 사용가능

- 형식

for 임시 상수 in 시퀀스 아이템 { 실행 코드 }

1
2
- 예제

for i in 0…2 { print(i) }

1

for i in 0…5 { if i.isMutable(of: 2) { print(i) continue // continue 키워드를 사용하면 바로 다음 시퀀스로 건너뜀 }

print(“(i) == 횰수”) // continue때문에 if문 밖에 있어도 출력안됨 }

1
2
3
4
### while 구문
- 특정 조건이 성립하는 한 블록 내부의 코드를 반복해서 실행

var names = [“a”, “b”]

while names.isEmpty == false { print(names.removeFirst()) // removeFirst()는 삭제함과 동시에 해당 요소 반환 }

1
2
3
4
### repeat-while 구문
- 최초 1회는 내부 코드를 실행하고 다음 조건이 성립하면 while 반복

var names = [“a”, “b”]

repeat { print(names.removeFirst()) } while names.isEmpty == false

1
2
3
4
5
6
7
8

<br>


# 함수(=메서드)

- 형태

func 함수이름(매개변수) -> 반환타입 { 실행 구문 return 반환 값 }

1
2
3
4
### 매개변수
- 가변 매개변수

func sum(_ numbers: Int…) -> Int { var total = 0 for number in numbers { total += number } return total }

let result1 = sum(1, 2, 3, 4) // 10 let result2 = sum() // 0

1
2
3
 - 입출력 매개변수(inout)
외부에 정의된 변수를 함수 내부에서 참조형태로 받기때문에 변경된 내용을 적용할 때 사용하는 키워드

func changeNum(number: inout Int) { number += 1 } var num = 0 changeNum(number: &num) print(num) // 1

1
2
3
4
5
6
7
8
9
10
- 매개변수 타입 앞에 `inout` 사용
- 함수에서 사용시 `&`사용


### 데이터 타입으로서의 함수
스위프트에서 함수도 데이터 타입으로 취급됨
즉, 함수는 값처럼 변수에 저장하거나, 매개변수로 전달하거나, 반환값으로 사용

- 함수 타입 문법
(매개변수 타입들) -> 반환 타입

(Int, Int) -> Int : 두 개의 Int를 받아 Int를 반환하는 함수 타입 (String) -> Void : String 하나 받아서 반환값 없는 함수 타입

1

// 두 정수를 더하는 함수 func add(a: Int, b: Int) -> Int { return a + b }

// 함수 타입 변수에 함수 할당 var mathFunction: (Int, Int) -> Int = add

// 변수로 함수 호출 let result = mathFunction(3, 5) // 8

// 함수 타입 매개변수 사용 func operate(_ a: Int, _ b: Int, using operation: (Int, Int) -> Int) -> Int { return operation(a, b) }

let sum = operate(4, 7, using: add) // 11 let product = operate(4, 7, using: { $0 * $1 }) // 28

1
2
3
4
5
6
함수 자체가 (Int, Int) -> Int 타입 값을 가질 수 있음
함수를 변수에 저장하거나, 다른 함수 매개변수로 넘길 수 있음 (고차함수)
클로저 문법으로 직접 함수 값을 만들어 전달 가능

- 함수 반환 타입 문법
(매개변수 타입) -> (반환할 함수 타입)

// 두 정수를 더하는 함수 반환 func makeAdder(_ x: Int) -> (Int) -> Int { func adder(y: Int) -> Int { return x + y } return adder }

let addFive = makeAdder(5)
let result = addFive(3) // 8

1
2
3
4
5
6
- 설명
makeAdder는 Int 하나 받아서 Int 하나 받는 함수 (Int) -> Int를 반환
반환된 adder 함수는 외부 변수 x를 캡처해서 더함
addFive는 5를 더하는 함수가 됨

func makeMultiplier(_ x: Int) -> (Int) -> Int { return { y in x * y } }

let multiplyByThree = makeMultiplier(3) multiplyByThree(4) // 12

1
2
3
4
5
6
7
<br>

# 옵셔널
- 스위프트의 특징 중 하나인 안전성을 문법으로 담보하는 기능, 값이 있을 수도 있고 없을 수도 있음을 나타내는 표현
오류가 발생하는 예제
옵셔널 타입은 기존의 타입에 `?`를 붙이고 `nil`을 할당 가능

var name: String? = “sangjin” name = nil

1
2
3
- 옵셔널은 열거형
타입이 옵셔널일 경우 스위프트 라이브러리에는 열거형으로 구현되어 있음

public enum Optional { case none case some(Wrapped) }

1
2
3
4
5
6
이때 `none`은 `nil`과 같은 것이고 실제 값은 연관값인 some의 Wrapped에 할당됨

## 옵셔널 추출
### 강제 추출
이 방법은 간단하지만 가장 위험한 방식
추출 시 옵셔널 값 뒤에 `!`를 붙여서 추출하지만 값이 없을 경우 런타임 에러 발생

var name: String? = nil

print(name!) // 에러 발생

1
2
3
### 옵셔널 바인딩
`if let`문 사용

var myName: String? = nil

if let name = myName { // 값이 있을 경우 임시 상수에 할당되고 값 출력 print(name) } else { // nil일 경우 print(“값이 없음”) }

1
2
3
4
5
6
<br>

# 구조체와 클래스
## 구조체
- 형태

struct 구조체이름 { 프로터티들 메서드들 }

1
2
- 예제

struct Person { var name: String var planet: String = “Earth”

func sayName() { print(self.name) } }

1
2
3
4
### 초기화
  이니셜라이저는 멤버변수들로 인해 자동으로 생성됨
  `var`로 구조체 인스턴스를 생성하면 내부 변수 변경 가능

struct Person { var name: String }

var p = Person(name: “Tom”) p.name = “Jane” // 변경 가능

1
2
  `let`으로 인스턴스를 생성하면 내부 변수 변경 불가

struct Person { var name: String }

let p = Person(name: “Tom”) p.name = “Jane” // 오류: Cannot assign to property: ‘p’ is a ‘let’ constant

1
2
3
4
5
6



## 클래스
- 형태

class 클래스이름 { 프로퍼티들 메서드들 }

// 상속받는 경우 class 클래스이름: 부모클래스 이름 { 프로퍼티들 메서드들 }

1
2
- 예제

class Person { var name: String var planet: String = “Earth”

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

func sayName() { print(self.name) } }

1
2
3

### 초기화

let person = Person(name: “sangjin”) var name = person.name person.sayName()

1
2
3
4
- 클래스의 소멸
클래스가 메모리에서 해제될때는 기본적으로 정의되어있는 `deinit` 메서드를 호출하지만 이는 클래스 정의에서는 생략
하지만 해제될 시점에 어떤 로직을 실행하고 싶을 경우 클래스에서 구현

class Person { …

deinit { print(“메모리 해제됨”) } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

### 구조체, 클래스 공통점과 차이점

- 공통점
1. 둘 다 사용자 정의 데이터 타입
2. 변수와 상수를 가질 수 있음
3. 함수를 가질 수 있음
4. 기본 생성자 자동 제공
5. 둘다 프로토콜 채택 가능
6. 모두 메모리 할당 필요
7. 직접 비교 연산자 구현 가능
8. `extension`으로 확장 가능



- 차이점
1. 구조체는 값 타입, 클래스는 참조 타입
2. 구조체는 프로퍼티를 변경할 경우 함수에 `mutating`키워드 필요
3. 구조체는 상속 불가능, 클래스는 상속 가능
4. 구조체는 스택 또는 최적화된 위치에 할당, 클래스는 힙에 할당 및 ARC 관리
5. 타입캐스팅은 클래스 인스턴스에만 허용
6. 참조 횟수 계산은 클래스의 인스턴스에만 적용




### 값 타입과 참조 타입의 차이
값 타입: 전달인자로 값을 넘길 때 전달될 값이 복사되어 넘겨짐
참조 타입: 값을 넘길 때 전달될 값의 메모리 주소가 전달됨

클래스의 인스턴스끼리 참조가 같은지 확인할 때는 `식별 연산자`를 사용하여 두 참조가 같은 인스턴스를 가리키고 있는지 비교

var sangjin = Person() var me = sangjin let another = Person()

print(sangjin == me) // true print(sangjin === another) // false orint(me !== another) // true

1
2
3
4
5
6
7
8


<br>

# 프로퍼티와 메서드

## 저장 프로퍼티 : 인스턴스의 변수 or 상수

var age: Int = 28 var name: String = “sangjin”

1
2
3
- 지연 저장 프로퍼티 : 해당 변수를 사용할 때 할당되는 프로퍼티
상수는 인스턴스가 완전히 생성되기 전에 초기화해야 하므로 지연 저장 프로퍼티는 무조건 변수로 선언

lazy var name: String = “sangjin”

1
2
3
4

## 연산 프로퍼티 : 특정 연산을 실행한 결과값
- 형태

변수키워드 변수이름: 변수 타입 { 연산자이름 { 수행할 연산 } }

1
2
- 예제

var _name: String = “” // 실제 값을 저장하는 저장 프로퍼티

var name: String { get { return _name } set(new) { _name = new } }

1
2
3
4
5
6
### 프로퍼티 감시자 : 프로퍼티의 값 변경을 감지하여 로직 수행
- 저장, 연산 프로퍼티에 적용 가능
- `willSet` : 전달인자로 변경될 값이 전달됨
- `didSet` : 전달인자로 변경되기 전의 값이 전달됨
- 예제

class Account { var credit: Int = 0 { willSet { print(“(credit) -> (newValue) 변경”) }

1
2
3
didSet {
  print("\(oldValue) -> \(credit) 변경")
}   } } ``` > **입출력 매개변수와 프로퍼티 감시자**   > 감시자가 있는 프로퍼티를 함수의 매개변수로 전달한다면 함수가 종료되는 시점에 값을 쓰기 때문에 항상 감시자를 호출

타입 프로퍼티 : 특정 타입에 사용되는 프로퍼티(static 변수)

  • 인스턴스가 생성되었을 때 사용할 수 있는 프로퍼티가 아닌 타입 자체에 속하고 영향을 미치는 프로퍼티
  • 인스턴스 생성 여부와 상관없이 타입 프로퍼티 값은 하나이며, 그 타입의 모든 인스턴스가 공통으로 사용하는 값
  • 저장 타입 프로퍼티 : 변수 또는 상수로 선언, 반드시 초깃값을 설정해야 하며 지연 연산됨 (다중 스레드여도 한 번만 초기화됨, lazy키워드 없이도 알아서 지연 처리)

  • 연산 타입 프로퍼티 : 변수로만 선언

예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class AClass {
  // 저장 타입 프로퍼티
  static var typeProperty: Int = 0

  // 저장 인스턴스 프로퍼티
  var instanceProperty: Int = 0 {
    didSet {
      Self.typeProperty = instanceProperty + 100
    }
  }


  // 연산 타입 프로퍼티
  static var typeComputedProperty: Int {
    get {
      return typeProperty
    }

    set {
      typeProperty = newValue
    }
  }
}

AClass.typeProperty = 123

let classInstance: AClass = AClass()
classInstance.instanceProperty = 100

print(AClass.typeProperty) // 200
print(AClass.typeComputedProperty) // 200

키 경로

  • 함수는 일급시민으로서 상수나 변수에 참조를 할당 가능 ``` func someFunction(paramA: Any, paramB: Any) { print(“someFunc”) }

var functionReference = someFunction(paramA:paramB) // 참조 할당

// 나중에 호출 functionReference(“A”, “B”)

// 같은형태의 다른 함수 참조 할당 functionReference = anotherFunc(paramA:paramB:)

```

This post is licensed under CC BY 4.0 by the author.