---
# System prepended metadata

title: 토요스터디_A반
tags: [study]

---

###### tags: `study`

# 🔥불타는 토요스터디_A반🔥

## 📖 목차
🔥 [소개](#-소개) <br>
👨‍💻 [팀원](#-팀원) <br>
🚫 [규칙](#-규칙) <br>
🗓️ [Week 링크](#-Week-링크) <br>


</br>

## 🔥 소개
매주 토요일 공부하는 커리어 스타터 9기 토요스터디 A반의 저장소

</br>

## 👨‍💻 팀원
| 🚩Zion🚩 | 🕷️Jusbug🕷️ |
| :--------: | :--------: |
| <Img src =  "https://hackmd.io/_uploads/ryoFC_LV2.png" width="200" height="200"> | <Img src = "https://hackmd.io/_uploads/Sy9vLlzin.jpg"  width="200"  height="200">|
|[Github Profile](https://github.com/LeeZion94) | [Github Profile](https://github.com/JusBug) | 

| 🦊Yetti🦊 | 🐹yy-ss99🐹 |
| :-------: | :--------: |
| <Img src = "https://hackmd.io/_uploads/BJJ5FYIVh.png"  width="200" height="200"> | <Img src = "https://hackmd.io/_uploads/B12OrKI43.png"  width="200" height="200"> |
|[Github Profile](https://github.com/iOS-Yetti) |[Github Profile](https://github.com/yy-ss99) |

| 🥭redmango🥭 | 😈MINT😈 |
| :--------: | :--------: |
|  <Img src = "https://hackmd.io/_uploads/SkkQtd8E3.jpg"  width="200" height="200"> | <Img src = "https://hackmd.io/_uploads/SJDOddL42.png"  width="200" height="200"> |
|[Github Profile](https://github.com/redmango1447) | [Github Profile](https://github.com/mint3382) |

<br>

## 🚫 규칙
### 매주 토요일 10:00 ~
❗️토요 스터디용으로 나오는 주제를 한 주 씩 느리게 진행합니다.
❗️주어진 1주일 간 발표자는 발표를 준비합니다.
❗️발표자가 아니라도 미리 주제에 따라 실험을 진행해 옵니다.

❌ 지각한 사람은 다음 주 발표자가 됩니다! ❌

<br>

## 🗓️ Week 링크
|Week N | 주제 | 발표자 |
|:--:|:--:| -- |
| 1 | [Optional / 예외처리](https://github.com/LeeZion94/StudyA/tree/master/Week1) | |
| 2 | [Type](https://github.com/LeeZion94/StudyA/tree/master/Week2) | |
| 3 | [MVC](https://github.com/LeeZion94/StudyA/tree/master/Week3) | |
| 4 | [Singleton Pattern / View Life Cycle](https://github.com/LeeZion94/StudyA/tree/master/Week4) | |
| 5 | [Singleton Pattern / View Life Cycle](https://github.com/LeeZion94/StudyA/tree/master/Week5) | 😈MINT😈 |
| 6 | [UML(Unified Modeling Language)](https://github.com/LeeZion94/StudyA/tree/master/Week6) | 🚩Zion🚩 |
| 7 | [SOLID 원칙](https://github.com/LeeZion94/StudyA/tree/master/Week7) | 🥭redmango🥭 |
| 7 | [고차함수](https://github.com/LeeZion94/StudyA/tree/master/Week8) | 🐹yy-ss99🐹 |



</br>

# 1주차 : Optional / 예외처리

### STEP 1: optional 값을 사용해봅시다.
#### 1. optional binding
    
```swift=
var productsList: [String?] = ["볼펜", "텀블러", "다이어리", "에코백", "머그컵", "후드집업"]

func showProductsListWithIfLet() {
    for index in 0...(productsList.count - 1) {
        if let product = productsList[index] {
            print("\(index)번 상품은 \(product)입니다.")
        } else {
            print("\(index)번 상품은 없습니다.")
        }
    }
}

func showProductsListWithGuardLet() {
    for index in 0...(productsList.count - 1) {
        guard let product = productsList[index] else {
            print("\(index)번 상품은 없습니다.")
            return
        }
        print("\(index)번 상품은 \(product)입니다.")
    }
}
``` 
#### 2. nil coalescing operator </summary>

```swift=
func showProductsListNilCoalescing() {
    for index in 0...(productsList.count - 1) {
        print("\(index)번 상품은 \(productsList[index] ?? "재고가 없는 상태")입니다.")
    }
}
```
#### 3. optional pattern
    
```swift=
func optionalPattern() {
    for (index, product) in productList.enumerated() {
        switch product {
            case .some(let product):
                print("\(index)번 상품은 \(product)입니다.")
            case .none:
                print("\(index)번 상품은 없는 상품입니다.")
        }
        
        if case let product? = product {
            print("\(index)번 상품은 \(product)입니다.")
        }
    }
}
```

#### 4. compactMap

```swift=
func printListWithCompactMap() {
        let products = productsList.compactMap{ $0 }
        for index in 0...products.count - 1 {
            print("\(index)번 상품은 \(products[index])입니다.")
        }
    }
```

### STEP 2: Optional을 활용한 경우 예외사항을 처리해 봅시다.

#### 1. do-catch, throw

``` swift
var budget: Int = 2000
var productsList: [String?] = ["볼펜", "텀블러", "다이어리", "에코백", "머그컵", "후드집업"]

enum productError: Error {
    case noProduct
    case noMoney
}

func buy(productNumber: Int) throws {
    let maxProductNumber = productsList.count
    
    if productNumber >= maxProductNumber || productNumber < 0 {
        throw productError.noProduct
    }
        
    guard let product = productsList[productNumber] else {
        throw productError.noProduct
    }
    
    if budget >= 1000 {
        budget -= 1000
    } else {
        throw productError.noMoney
    }
    
    productsList[productNumber] = nil
    
    print("\(product)를 구매하셨습니다.")
    print("현재 잔액은 \(budget)입니다.")
}

func checkBuy() {
    do {
        try buy(productNumber: 1)
        try buy(productNumber: 2)
        try buy(productNumber: 3)
    } catch productError.noProduct{
        print("재고가 없습니다.")
    } catch productError.noMoney{
        print("돈이 부족합니다.")
    } catch {
        print("뭐야 이거 왜이래")
    }
}

```

#### 2. Result Type
    
```swift=
enum BuyError: Error {
    case wrongGoodsNumber
    case soldOut
    case insufficientBalance
}

var budget: Int = 2000
var productsList: [String?] = ["볼펜", "텀블러", "다이어리", "에코백", "머그컵", "후드집업"]

func verifyPurchaseOfGoods(_ goodsNumber: Int) -> Result<Bool, BuyError> {
    guard 0...5 ~= goodsNumber else { return .failure(.wrongGoodsNumber) }
    guard productList[goodsNumber] != nil else { return .failure(.soldOut) }
    guard budget >= 1000 else { return .failure(.insufficientBalance) }
    
    return .success(true)
}

func processPurchaseOfGoods(_ goodsNumber: Int) {
    print("상품구매를 완료하였습니다. : \(productList[goodsNumber] ?? "정보없음")")
    productList[goodsNumber] = nil
    budget -= 1000
}

func printErrorMessage(_ error: BuyError) {
    switch error {
        case .wrongGoodsNumber:
            print("유효하지 않은 상품번호입니다.")
        case .soldOut:
            print("상품 매진입니다.")
        case .insufficientBalance:
            print("잔액이 부족합니다.")
    }
}

func purchase(productNumber: Int) {
    let resultPurchaseOfGoods = verifyPurchaseOfGoods(productNumber)
    
    switch resultPurchaseOfGoods {
        case .success:
            processPurchaseOfGoods(productNumber)
        case .failure(let error):
            printErrorMessage(error)
    }
}

for randomNumber in [6, 1, 2, 3, 0, 4, 1, 2] {
    purchase(productNumber: randomNumber)
}
```
    
# 2주차: Type

### Type을 정의해서 인스턴스가 서로 객체지향적으로 소통할 수 있도록 해보자.

* 주요 개념: `상수`, `변수`, `함수`, `Optional`

![](https://hackmd.io/_uploads/HyvyTNXN2.png)
    
#### 🫠Object-Oriented Programming, OOP
컴퓨터 프로그램을 객체들의 유기적인 협력과 결합으로 본다는 패러다임.
객체들을 만들고 이 객체들을 조립해서 하나의 완성된 프로그램을 만든다.
    
🌸특징
    - 추상화
    - 캡슐화
    - 은닉화
    - 상속성
    - 다형성

🌸장점
    - 각자 독립적인 역할을 가지기에 코드의 변경을 최소화하고 유지보수를 하는 데 유리하다. 
    - 코드의 재사용을 통해 반복적인 코드를 최소화한다.
    - 코드를 최대한 간결하게 표현할 수 있다.
    - 인간 친화적이고 직관적인 코드

#### 예제 코드

```swift=
class Shop {
    var productsList: [String?] = ["볼펜", "텀블러", "다이어리", "에코백", "머그컵", "후드집업"]
    var revenue: Int = 0
}

class ShopManager {
    var shop: Shop
    
    init(shop: Shop) {
        self.shop = shop
    }
    
    func checkProduct(_ productNumber: Int) -> Bool {
        var canBuy: Bool
        if productNumber > shop.productsList.count - 1 {
            print("상품이 존재하지 않습니다.")
            canBuy = false
        } else if shop.productsList[productNumber] != nil {
            canBuy = true
        } else {
            print("상품이 품절 되었습니다.")
            canBuy = false
        }
        return canBuy
    }
    
    func takeBudget(_ productNumber: Int) {
        shop.productsList[productNumber] = nil
        shop.revenue += 1000
    }
}


class Camper {
    var budget: Int
    
    init(budget: Int) {
        self.budget = budget
    }

    func buy(productNumber: Int, manager: ShopManager) {
        if manager.checkProduct(productNumber) {
            print("\(productNumber)번 주세요.")
            checkBudget(productNumber, manager)
        }
    }
    
    private func checkBudget(_ productNumber: Int, _ manager: ShopManager) {
        if budget < 1000 {
            print("예산이 부족하네요.")
        } else if let product = manager.shop.productsList[productNumber] {
            print("\(product)을/를 구매했어요!")
            payBudget(productNumber, manager)
        }
    }
    
    private func payBudget(_ productNumber: Int, _ manager: ShopManager) {
        budget -= 1000
            
        print("잔액이 \(budget) 만큼 남았습니다.")
        manager.takeBudget(productNumber)
    }
}
                        
```
        
### Delegation Pattern
#### 기본 예시 코드
    
```swift=
protocol ProductSelectionDelegate {
    func didSelectProduct(imageName: String)
}

class BossViewController {
    var delegate: ProductSelectionDelegate?
    
    @objc func iphoneButtonTapped() {
        delegate?.didSelectProduct()
    }
    
}

class InternViewController: ProductSelectionDelegate {
    func presentBossViewController() {
        let bossViewController = BossViewController()
        bossViewController.delegate = self
        
        self.present(bossViewController)
    }
    
    func didSelectProduct(imageName: String) {
        // Statesments -> Select Product
    }
}
``` 
* 위의 예시는 BossViewController에서 iphone모양의 Button이 눌렸을 때 BossViewController에서 InterViewController로 String과 함께 event를 전달하는 방식을 Delegation Pattern을 사용해서 나타냈습니다. InterViewController는 ProductSelectionDelegate라는 protocol로 추상화 레이어로 한번 감싸져 있습니다. BossViewController의 delegate로 할당되어 이벤트를 전달받을 때 사용했습니다. BossViewController에서는 InterViewController가 protocol로 감싸져있기 때문에 didSelectProduct 메서드만 접근이 가능합니다.
    
    
# 3주차: MVC
## 🔥실험 목표
    
- 하나의 앱을 구성하고 MVC 구조를 이해할 수 있습니다.
- Notification Center를 활용해 이벤트를 처리할 수 있습니다.
    
* 주요 개념: `IBOutlet`, `IBAction`, `UIElement`, `Notification`


## 🗒️ TODO
### 실험 1.
- 이름과 전화번호를 TextField에 작성하고, 지금 등록하기 버튼을 누르면 등록자 배열에 저장됩니다.
- 등록 확인하기 버튼을 누르면 등록 정보에 작성 내용이 표시됩니다.
- Storyboard에 위치해있는 화면 요소를 코드 영역에서 활용해봅시다.
- '이름, 전화번호’를 프로퍼티로 갖는 구조체 Registrant 구조체를 생성해봅시다.
- 이름, 전화번호 TextField 에 입력한뒤 지금 등록하기 버튼을 누르면 모델 타입(Registrant)의 인스턴스가 생성될 수 있도록 합시다.
- 등록확인하기 버튼을 누르면 등록 정보의 '준비중입니다.' Label에 새로 등록한 정보가 표현되도록 구성합시다.
    
### 실험 2.
- 주석을 사용해 ViewController 파일 내부에서 View와 Controller를 나누어봅시다.
- 왜 Cocoa MVC 패턴은 View와 Controller를 분리하기 어려운 것인지, 설명해봅시다.
- StoryBoard는 MVC 패턴의 관점에서 View라고 볼 수 있을까요? 자신만의 근거를 들어 생각해봅시다.

### 실험 3.
- 때로는 인스턴스의 프로퍼티가 변경되었을 때, 프로퍼티의 외부에서 이를 알아차리고 동작을 수행해야하는 경우가 있습니다.
- 우리는 기존에 IBAction을 활용해 버튼을 눌렀을 때 Label에 값이 들어가도록 구현하였습니다. 이번에는 같은 동작을 Notification Center를 활용해서 구현해봅시다.
- 등록 확인하기 버튼은 다음 실험에서 사용하지 않습니다.
- Notification Center를 생성하고, Observer를 등록합니다.
- 지금 등록하기 버튼을 눌렀을 때, NotificationCenter에 알림이 전송됩니다.
- Observer를 활용해 Label에 원하는 값을 표현합시다.
    

## 😎코드 예시

#### 1. 모델 타입을 활용해 앱을 구상해보자. 

```swift=
import UIKit

struct Registrant {
    var name: String
    var phoneNumber: String
}

class ViewController: UIViewController {
    
    @IBOutlet weak var nameTextField: UITextField!
    @IBOutlet weak var phoneNumberTextField: UITextField!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var phoneNumberLabel: UILabel!
    
    var registrantList: [Registrant] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
    @IBAction func hitRegisterButton(_ sender: Any) {
        register()
    }
    
    @IBAction func hitCheckButton(_ sender: Any) {
        nameLabel.text = registrantList.last?.name
        phoneNumberLabel.text = registrantList.last?.phoneNumber
    }
    
    func register() {
        guard let name = nameTextField.text,
              let phoneNumber = phoneNumberTextField.text else {
           return
        }
        
        let registrant = Registrant(name: name, phoneNumber: phoneNumber)
        registrantList.append(registrant)
    }
    
    @objc func changeLabel() {
        nameLabel.text = registrantList.last?.name
        phoneNumberLabel.text = registrantList.last?.phoneNumber
    }
}

```
        
#### 2. view controller를 view와 controll로 구분해보자. 

- Cocoa MVC에서는 이 둘을 따로 놓고 보기 힘들다고 판단했습니다. 이미 제공해주는 템플릿자체가 둘의 관계가 밀접한 관계를 가지고 있다고 생각합니다. 그 이유는 이미 ViewController내부에서 Storyboard의 UI Component들을 관리 할 수 있는 IBOutlet과 이벤트를 처리할 수 있는 IBAction이 존재하기도 하고 코드 베이스를 사용하여 UI를 구현하는 경우 또한 존재하니 Storyboard를 View와 동일시 하는 것은 무리가 있다고 생각합니다. 
    
- 또한, 모델의 데이터가 갱신되어 View를 갱신하여야 하는 경우 결국 갱신된 데이터는 ViewController에서의 UI Component를 다루는 IBOutlet을 통해 갱신되어지므로 온전히 Storyboard가 View와 동일시 되는 것은 불가하다 생각합니다.

#### 3. Notification Center를 활용해 변화를 감지해보자.
보통 Notification Center는 화면과 화면 사이에서 사용한다. 
다만 이번 스터디 예제에서는 한 화면에서 버튼으로 구현하는 방법으로 연습하였다. 

```swift=
    struct Registrant {
    var name: String? = "준비중입니다"
    var phoneNumber: String? = "준비중입니다"
}

class ViewController: UIViewController {
    
    @IBOutlet weak var nameTextField: UITextField! //view
    @IBOutlet weak var phoneNumberTextField: UITextField! //view
    @IBOutlet weak var nameLabel: UILabel! //view
    @IBOutlet weak var phoneNumberLabel: UILabel! //view

    var registrantList: [Registrant] = [] // Model
    
    override func viewDidLoad() { //view
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    
        NotificationCenter.default.addObserver(self, selector: #selector (register), name: Notification.Name("register"), object: nil)
        
    }

    @IBAction func hitRegisterButton(_ sender: Any) {
        var member: Registrant = Registrant()
        
        member.name = nameTextField.text
        member.phoneNumber = phoneNumberTextField.text
        
        registrantList.append(member)
        
        NotificationCenter.default.post(name: Notification.Name("register"), object: registrantList)
        
//        NotificationCenter.default.removeObserver(self, name: Notification.Name("register"), object: nil)
        
    }
    
    @objc func register() {
        nameLabel.text = registrantList.last?.name
        phoneNumberLabel.text = registrantList.last?.phoneNumber
    }
}

```

    
# 4주차: Singleton Pattern / View Life Cycle
## 🔥실험 목표
    
- View Life Cycle과 관련된 메서드가 언제, 어떤 상황에서 호출되는지 알 수 있다.
- Singleton Pattern을 활용해 다수의 View Controller가 정보를 공유하도록 할 수 있다.
* 주요 개념: `Singleton Pattern`, `view Life Cycle`

#### view life cycle 

- 뷰의 생명주기(view life cycle)는 뷰가 언제화면에 나타나고 언제 사라지는지에 대한 주기를 나타낸 것이다.
- 아래의 코드로 구현되는 순서를 보자면 `viewDidLoad 1 -> viewWillAppear 1 -> viewDidAppear 1 -> viewWillDisAppear 1 -> viewDidDisAppear 1` 이 순서로 구현된다.
- 뷰 컨트롤러가 하나 더 있고 그 곳은 `2`라고 가정했을 때 실행순서를 살펴보자.
    - 1번째 화면 
    `viewDidLoad 1 -> viewWillAppear 1 -> viewDidAppear 1`
    - 2번째 화면으로 넘어감
    `viewDidLoad 2 -> viewWillDisappear 1 -> viewWillAppear 2 -> viewDidAppear 2 -> viewDidDisappear 1`
    - 2번째에서 다시 1번째 화면으로 돌아옴
    `viewWillDisappear 2 -> viewWillAppear 1 -> viewDidAppear 1 -> viewDidDisappear 2`
위에서 순서를 보다시피 화면 전환이 이루어질 때 다음 화면이 나타나고 그 후에 이전 화면이 완전히 사라지는걸 알 수 있다. 또 하나의 특징으로는 1번째에서 2번째로 화면 전환이 일어날 때는 `viewDidLoad 2`가 항상 호출되지만 2번째에서 1번째로 넘어갈 때는 `viewDidLoad 1`은 호출되지 않는다는 점이다. 이는 'viewDidLoad 1'은 뷰의 계층이 메모리에서 해제되는 시점이 없기 때문이다. 
    
    

```swift=
class MainViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        print("viewDidLoad 1")
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("viewWillAppear 1")
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("viewDidAppear 1")
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        print("viewWillDisappear 1")
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        print("viewDidDisappear 1")
    }
}
```
        
#### Singleton Pattern 
    
## 싱글톤패턴
- 객체의 인스턴스가 오직 1개만 생성되는 패턴
    
### **장점**

- 한 번 생성된 인스턴스로 계속하여 활용하기 때문에 메모리를 효율적으로 관리할 수 있다.
- 단 하나만 존재하기 때문에 동일한 데이터와 서비스를 다양한 객체에 제공할 수 있다.
- 인스턴스가 하나라는걸 보장하기 때문에 Thread Safe하다.

### **단점**

- 싱글톤의 인스턴스가 너무 많은 일을 하거나, 많은 데이터를 공유시킬 경우 다른 클래스의 인스턴스들과의 결합도가 높아져 `개방 - 폐쇄 원칙(OCP, Open-Closed Principle)`을 위배함 (객체 지향 설계 원칙)
- 전역처럼 가져다가 쓸 수 있다보니 값의 변경점을 찾는 부분이 어려워진다.(안티패턴)
- static으로 선언되기 때문에 할당되는 메모리 자체가 프로그램이 끝날 때까지 잡혀있다.
- 싱글톤이 활용되는 곳이 많다면 유지보수, 확장이 어려울 수 있다.
    
### **언제 사용해야 하는가**
- 여러 ViewController에서 전역적으로 싱글톤 타입에 접근해야 하는 경우
- 1개의 인스턴스를 생성해서 일관된 프로퍼티를 사용하고 싶은 경우
- 메모리 낭비를 방지하고 싶은 경우
    
### 사용 예시

```swift=
class PointManager {
    static let shared = PointManager()
    
    var point = 50
    
    private init() {}
}

class MainViewController: UIViewController {
    @IBOutlet weak var pointLabel: UILabel!
    
    let pointInfo = PointManager.shared
    
    @IBAction func tappedPointResetButton(_ sender: Any) {
        pointLabel.text = String(pointInfo.point)
    }
}

class BuyingViewController: UIViewController {
    
    let pointInfo = PointManager.shared
    
    @IBAction func tappedPurchaseButton(_ sender: Any) {
        pointInfo.point -= 50
        dismiss(animated: true, completion: nil)
    }
}

    
```
    
    
# 5주차: Singleton Pattern / View Life Cycle
## 🔥실험 목표
    
- View Life Cycle과 관련된 메서드가 언제, 어떤 상황에서 호출되는지 알 수 있다.
- Singleton Pattern을 활용해 다수의 View Controller가 정보를 공유하도록 할 수 있다.
* 주요 개념: `Singleton Pattern`, `view Life Cycle`
 
![](https://hackmd.io/_uploads/r1vXiJkL3.png)
    싱글톤이란 특정 용도로 객체를 단 하나만 생성해 공용으로 사용하는 디자인 패턴이다.
    어디서 호출하든지 전부 다 한 곳만 바라보고 있으며 마치 전역 변수와 같이 사용할 수 있다. 
    `유일한`
![](https://hackmd.io/_uploads/By0ViJyL2.png)
    어디서든 누구나 불러서 고칠 수 있는 민트 하우스! 나무는 무엇으로? 유리는 무엇으로?
    static을 사용해서 사용 시점에 초기화 해주며 private이 붙어서 다시 초기화할 수 없다.
![](https://hackmd.io/_uploads/rJ0rjk1Ih.png)
    1. 메모리를 단 한 번만 사용하기에 메모리 관리가 용이하고, 메모리릭 즉 메모리 누수가 발생하지 않는다. 
    -> 메모리 누수는 개발자가 의도하지 않은 메모리를 점유하고 있는 현상
    -> 보통 콜렉션에 저장한 객체를 해지하지 않고 계속 유지하며 발생.
    -> 싱글턴은 단 하나의 객체만 메모리에 올라가 있으니 의도하지 않은 것은 존해할 수 없음.
    2. 객체 접근 시간이 짧아진다. 
    객체를 한 번만 만들어놓고 접근하는 방식이라 생성 시 메모리를 할당하고 초기화하는 순간들이 생략되면서 짧아진다
    3. 전역(static) 범위이라 데이터 전달이 쉽다.
    어디서든 접근 가능, 데이터 공유 적절.
    
### 논의 1. static의 메모리 할당 시점
    
Zion: 전역 및 정적 변수의 메모리할당은 data 영역에서 이루어집니다. 따라서 static 메모리가 할당되는 시점은 프로그램을 시작할 때 할당되고 프로그램이 종료될 때 해제됩니다.
    
Mint: 호출되거나 만들어지는 시점에 메모리에 할당됩니다.
    
결론: Mint승
Swift에서 global과 static은 lazy한 initiazlizer를 가집니다. 처음으로 호출되거나 사용되어 질 때 메모리에 할당됩니다.

![](https://hackmd.io/_uploads/H1o8jJkL2.png)
- DIP [링크](https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EC%95%84%EC%A3%BC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-DIP-%EC%9D%98%EC%A1%B4-%EC%97%AD%EC%A0%84-%EC%9B%90%EC%B9%99)
    객체에서 어떤 class를 참조해서 사용해야하는 상황이 생긴다면, 그 class를 직접 참조하지 말고 상위 요소를 참조해야 한다.
    : 하위 모듈에 사용자가 의존하게 되면, 하위 모듈에 변화가 있을 때마다 사용자의 코드를 자주 수정해야한다.
    -> 싱글톤에 다른 객체들이 의존하게 되면, 싱글톤 내용에 매번 변화가 있을 때마다 각 객체들이 수정을 해야한다. 

- OCP [링크](https://nesoy.github.io/articles/2018-01/OCP)
    기존의 코드를 변경하지 않으면서 기능을 추가할 수 있어야 한다.
    위에서 말한 DIP가 위반되어 싱글톤 내용에 매번 변화가 있을 때마다 각 객체들의 수정이 필요하다면 OCP 위반이 된다. 
      
- 하나의 인스턴스가 데이터를 공유하기에 데이터와 모델의 격리가 불가능하다.
    때문에 수정, 테스트가 어렵다. = 내부 속성 변경, 초기화 어렵다.
    인스턴스를 미리 다 받아서 설정이 끝난 상태이기에 유연한 테스트가 힘들다.

![](https://hackmd.io/_uploads/BkCws1kUh.png)
Thread-safe [링크](https://80000coding.oopy.io/e9007ef5-e020-4a0f-84d2-129e4b5d3c6f)
보통 싱글턴 패턴을 쓰다보면, Multi-Thread에서 문제가 생길 수 있다. [링크](https://velog.io/@hkh1213/%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%94%A9%EC%9D%98-%EC%9E%A5%EC%A0%90-%EB%8B%A8%EC%A0%90) Thread는 Process 내에서 실제로 작업을 수행하는 주체로 여러개가 있는 multi-Thread의 경우 각 thread들을 동시에 병행 실행한다. 그런데 이 경우 보통의 싱글턴 패턴에서는 문제가 생긴다. 인스턴스가 단 한 번만 만들어져야 하는데 여러개가 만들어져버리는 것이다. 
    
**이 경우를 Thread-safe 하지 못하다 라고 한다.**
_Thread-safe = 여러 thread가 접근했을 때 문제가 발생하지 않고 안전하다._

그런데 스위프트의 경우는 싱글턴을 사용할 때도 thread-safe하다!
static을 사용하기 때문인데 이를 사용하면 타입 프로퍼티로 선언이 된다. 
타입 프로퍼티는 인스턴스 프로퍼티와는 다르게 타입에서 바로 사용하는 것이다. 고정되는!
타입 프로퍼티는 메모리에 타입이 한 번 올라갔을 때부터는 계속해서 이 메모리를 공유한다. 
때문에 multi-Thread에서 여러 개의 객체가 생성되려해도 타입 프로퍼티기에 여러개의 인스턴스가 생성되지 않는다.
-> 즉 Thread-Safe하다.

![](https://hackmd.io/_uploads/BkEIZgkI2.png)
싱글톤은 안티패턴이라고 불리기도한다.
하지만 많은 사람들이 사용해서 정립한 양식인 패턴이 안티가 되는 것은 올바르지 않다.
즉, 싱글턴 패턴은 상황과 때에 맞게 올바르게 잘 사용하는 것이 중요하다.

싱글턴의 가장 큰 단점은 무분별하게 사용되어 예상치 못한 수정이 발생했을 때 어떤 부분에서 수정이 일어난 것인지 발견하기 어렵다는 단점이 있다.
따라서, 싱글턴은 무분별하게 사용되어서는 안된다.

하나의 인스턴스를 통해 관리가 되므로 그 목적과 용도에 맞게 사용되는 것이 중요하다.

![](https://hackmd.io/_uploads/HkrKoy1Uh.png)
view Life Cycle이란 뷰가 화면에 나타나고 사라지는 것에 대한 생명주기를 나타낸다. 
- viewDidLoad
- viewWillAppear
- viewDidAppear
- viewWillDisappear
- viewDidDisappear
사용할 때 앞에는 override를 붙여야 하고 밑에 super 키워드로 선언해 주어야 한다. 위의 메서드들은 해당 뷰 컨트롤러가 상속받고 있는 UIViewController에 구현되어 있다. override는 부모와 자식 간의 동일한 메서드를, 자식 클래스에서 대체해서 사용하는 경우에 선언한다. 이때 기존 UIViewController에 있는 메서드 기능들을 다시 선언해주어야 하기에 super 키워드를 사용한다. 

![](https://hackmd.io/_uploads/B1WcoykI3.png)
화면 전환 방식 중 modal Present에도 여러가지 스타일이 있다. 
- fullScreen: 전체 화면을 덮는 방식
- currentContext: full과 비슷하나 view의 위에 표시되는. 첫 view가 작아지면 다음 화면도 present 될 때 작은 view로 present
- pageSheet: 가로 방향으로 유지되는 더 큰 장치의 기본 콘텐츠를 부분적으로 덮음. 노출되지 않은 모든 영역은 상호 작용을 방지하기 위해 흐리게.
- formSheet: 화면 중앙에 표시, 노출되지 않은 모든 영역은 상호 작용을 방지하기 위해 흐리게. [링크](https://stackoverflow.com/questions/38584411/ios-page-sheet-form-sheet)

위와 같은 스타일들인데 fullScreen과 CurrentContext 처럼 앞선 뷰를 완전히 덮는 경우는 첫번째 뷰에서 두번째 뷰를 불렀다가 다시 첫번째 뷰로 돌아올 경우 첫번째 뷰가 다시 WillAppear하는 과정이 있지만 pageSheet와 formSheet처럼 기존 뷰가 흐리게 남아있는 경우는 다시 WillAppear하는 과정이 없다. 

### 논의 2. modalPresentationStyle중 fullScreen vs currentContext

fullScreen과 currentContext의 차이는 무엇을 기준으로 present를 할 것인지에 따라 달라집니다. fullScreen은 현재 나의 기기 Screen을 기준으로 present 하고 currentContext는 현재 present를 호출한 ViewController를 기준으로 해당 ViewController 내부에 present되게 됩니다. 

### 논의 3. FullScreen과 overFullScreen차이

- overFullScreen은 뷰의 alpha 처리가 가능합니다. FullScreen의 경우 알파값이 적용되지 않습니다. alpha 값을 세팅을 해도 불투명하게 보이게 됩니다. overFullScreen으로 설정시 뷰의 alpha값이 적용되어 반투명하게 보이게 됩니다.
- overFullScreen은 뷰 위에 새로운 뷰를 present 합니다. 때문에 뒤에 있던 뷰가 사라지지 않고 남게 됩니다.


[참조링크](https://magi82.github.io/ios-modal-presentation-style-01/)


![](https://hackmd.io/_uploads/SJTqikJL3.png)
화면 전환 방식 중 navigation Controller를 이용한 push도 있는데, 
- push를 통해 stack에 넣어 쌓고
- pop을 통해 stack에서 빼내면서 해제한다.

_이때 stack에서 가장 위에 있는 view가 화면에 보이는 view이다._


# 6주차: UML(Unified Modeling Language)
## 🔥실험 목표
- UML을 바탕으로 코드를 작성할 수 있다.

![](https://hackmd.io/_uploads/r1QpaAFUn.png)

- 개발자간의 의사소통이 필요한 이유는 협업을 위해서이다. 협업시 가장 위험한 것은 특정 주제에 대해서 모두가 같은 이해를 가지긴 힘들다는 것이다.
- 따라서 개발의 산출물을 가지고 같은 것을 보면서 논의 할 수 있어야 한다.
- 또한, 언어적인 특성을 가지고 있기 때문에 같은 부분이라고 해도 다른 표현을 사용해서 나타낼 수 있다.

![](https://hackmd.io/_uploads/SJ3_ACF82.png)
- Class 간의 Interface를 어떻게 가지고 가고 있는지 의존 및 연관관계가 어떻게 되어있는지를 확인할 때 주로 사용한다.
- 공통된 class diagram을 보면서 코드를 작성하기 전에 해당 기능 및 모듈이 올바르게 설계되어 있는지 리뷰를 하며 더 좋은 설계를 가져갈 수 있다.

![](https://hackmd.io/_uploads/HyuT0RYLh.png)
- 주로 특정 기능의 동작의 흐름을 메서드 수준에서의 구체적으로 알고 싶을 때 사용한다.
- class Diagram과는 다르게 기능의 순서 및 흐름을 이해하는데 주로 사용한다.
- 
![](https://hackmd.io/_uploads/HkJVXk583.png)
- 프로그램의 전체적인 흐름을 이해하는데 사용되어지는 diagram의 종류이다.
- 세부적인 기능사항 보다는 흐름자체에 집중할 수 있다.

![](https://hackmd.io/_uploads/S1jmNJqLn.png)
- 여러가지 중에서도 눈여겨 봐야할 건 각 class 간의 의존 및 연관 관계를 알 수 있는 Class Diagram이다.
- 코드를 작성하기 전에 구현할 기능 및 모듈의 설계에 대해 논의할 수 있다는 점에서 의미가 크다.
- 일반화, 실체와, 의존, 연관등 많은 요소들을 하나의 diagram에서 표현할 수 있다.
- input, output을 무조건적으로 가지는 프로그램의 특성상 한눈에 보기 쉽게 표현할 수 있다.

![](https://hackmd.io/_uploads/H1UmrJcIh.png)
- 의존
어떤 기능이나 행위를 하는 데에 있어서 필요한 인스턴스 및 객체가 있다면 우리는 그것에 의존한다.
어떤 클래스가 다른 클래스를 참조한다면 의존한다고 할 수 있다.
A 클래스가 B을 참조하여 여러 기능을 수행한다면 A는 B에 의존한다 할 수 있다.
초기화를 통해서 의존성이 주입이 되는 경우에도 초기화를 하기 위한 기능 즉, init에서 해당 객체가 필요한 것이므로 의존이 맞다.

- 연관
의존 보다는 조금 넓은 개념으로 사용된다.
해당 클래스에서 연관된 모든 것 들을 연관되었다고 표현한다.
또한 행위나 기능상에 호출되지 않았더라도 해당 클래스 외부에서 사용되어질 수 도 있으므로 의존과는 다른 의미이다.(A 클래스 내의 B 프로퍼티는 A 클래스에서 행위나 기능을하는데 사용되어지지 않으므로 의존관계라 할 수 없다. 하지만 A 클래스 외부에서 A 클래스를 통해 호출되어 사용되어지므로 A는 B와 연관관계이다.)

[금주의 실험 주제] : 작성된 UML을 바탕으로 코드를 작성해봅시다.
![](https://hackmd.io/_uploads/S1PvHy9Uh.png)


```swift
enum CoffeeMenu: String {
    case americano = "아메리카노"
    case latte = "라떼"
    
    var price: Int {
        switch self {
        case .americano:
            return 2000
        case .latte:
            return 2500
        }
    }
}

class Coffee {
    var kind: CoffeeMenu
    var amount: Int
    
    init(kind: CoffeeMenu, amount: Int) {
        self.kind = kind
        self.amount = amount
    }
}

class CoffeeShop {
    var manager: CafeManager
    
    init(manager: CafeManager) {
        self.manager = manager
    }
    
    func order(_ menu: CoffeeMenu) {
        manager.brew(menu)
    }
}
```

```swift
class Person {
    var name: String
    var money: Int
    var coffee: Coffee?
    
    init(name: String, money: Int) {
        self.name = name
        self.money = money
    }
    
    func buy(_ menu: CoffeeMenu, at shop: CoffeeShop) {
        if menu.price <= money {
            shop.order(menu)
            money -= menu.price
            return
        }
        
        print("잔액이 부족합니다.")
    }
}

class CafeManager: Person{
    func brew(_ menu: CoffeeMenu) {
        print("\(menu.rawValue)가 완성됬습니다.")
    }
}
```

![](https://hackmd.io/_uploads/H1_prJcLn.png)
- 소스코드의 의존성은 제어흐름에 따라 결정된다.
- 단순히 제어흐름에 의해 의존성이 향하는 것이 아닌 Interface 방향으로 의존성이 역전되기 때문이다.

# 7주차: SOLID 원칙
## 🔥실험 목표
SOLID 원칙을 준수하도록, 작성했던 코드를 리팩토링 할 수 있다.
<br>
![](https://hackmd.io/_uploads/B1SE2DZwn.png)
- SOLID는 5개의 프로그래밍 디자인 원칙의 앞글자를 딴 합성어이다. 각 디자인 원칙들은 소프트웨어의 이해와 발전뿐 아니라 유연성과 유지보수성을 높여준다
<br>

![](https://hackmd.io/_uploads/r1hxaPWP2.png)
- 특정 Class를 변경할 이유가 하나 이상이 되면 이는 하나 이상의 책임을 가진 것이라고 판단할 수 있다.
- SRP위반시 특정 함수 하나에 대한 변경이 다른 책임에 영향을 끼칠 수 있고 그 반대의 경우도 발생 가능하다. 이러한 책임의 중복들은 테스트의 가능성을 방해할 뿐 아니라 코드를 읽고 이해하는 것에도 방해가 된다.
<br>

![](https://hackmd.io/_uploads/B1EC6vZPn.png)
- 어떤 모듈의 기능을 하나 수정할 때, 그 모듈을 이용하는 다른 모듈들 역시 줄줄이 고쳐야 한다면 유지보수가 복잡할 것이다.
- 변경(확장)될 것과 변하지 않을 것을 엄격히 구분해 인터페이스를 정의하고 구체적인 타입 대신에 인터페이스에 의존하도록 코드를 작성해야한다.
- 인터페이스를 설계할 때 주의할 점이 있는데, 가능하면 변경되어서는 안되므로 여러 경우의 수에 대한 고려와 예측이 필요하며, 잘못 분리하면 관계가 더 복잡해질 수 있으므로 적당한 추상화 레벨을 선택해야 한다.
<br>

![](https://hackmd.io/_uploads/H1jDCPWv3.png)
- 상속관계에서는 꼭 일반화 관계 (IS-A) 가 성립해야 한다는 의미 (일관성 있는 관계인지)
- 상속관계가 아닌 클래스들을 상속관계로 설정하면, 이 원칙이 위배됨 (재사용 목적으로 사용하는 경우)
- 결국은, 리스코프 치환 원칙을 지키지 않으면 개방 폐쇄 원칙을 위반하게 되는 것이다.
<br>

![](https://hackmd.io/_uploads/SJ9Nk_-Ph.png)
- 각 클라이언트가 필요로 하는 인터페이스들을 분리함으로써, 클라이언트가 사용하지 않는 인터페이스에 변경이 발생하더라도 영향을 받지 않도록 만들어야 하는 것이 핵심이다.
<br>

![](https://hackmd.io/_uploads/BkgKk_bD2.png)
- 구체화된 클래스에 의존하기 보다는 추상 클래스나 인터페이스에 의존해야 한다는 뜻
- DIP은 컴파일 타임에서만 일어난다. 런타임에서는 의존관계가 바뀌지 않는다. 프로그래머가 코드를 더 유연하게 작성하기 위해 DIP이 적용되는 것일 뿐이다.
- YAGNI
**y**ou **a**in't **g**onna **n**eed **i**t.
"DIP이 필요 없을 것이다" 가 기본 가정이다.
필요할 때 구현해주면 된다.
(third-party library를 추가할 때에는 DIP을 적용하는 것이 좋다고 한다.)

## 총 정리
- SRP 와 ISP 는 객체가 커지는 것을 막아준다. 객체가 단일 책임을 갖도록 하고 클라이언트마다 특화된 인터페이스를 구현하게 함으로써 한 기능의 변경이 다른 곳까지 미치는 영향을 최소화하고, 이는 기능 추가 및 변경에 용이하도록 만들어 준다.

- LSP 와 DIP 는 OCP 를 서포트한다. OCP 는 자주 변화되는 부분을 추상화하고 다형성을 이용함으로써 기능 확장에는 용이하되 기존 코드의 변화에는 보수적이도록 만들어 준다. 여기서 '변화되는 부분을 추상화'할 수 있도록 도와주는 원칙이 DIP 이고, 다형성 구현을 도와주는 원칙이 LSP 인 것이다.

## 명심할 것
The SOLID principles are not rules. They are not laws. They are not perfect truths. - Uncle Bob(로버트 C. 마틴 - SOLID를 제시한 사람)
(SOLID 원칙은 규칙이 아닙니다. 그것들은 법이 아닙니다. 그것들은 완전한 진리가 아닙니다.)
[출처]https://sites.google.com/site/unclebobconsultingllc/getting-a-solid-start


# 8주차: 고차함수
## 🔥실험 목표
- 함수의 타입인 함수타입 을 이해할 수 있다.
- 클로저를 매개변수로 받는 함수를 정의할 수 있다.
- 고차함수의 작동 방식을 이해하고, 직접 내부를 구현해볼 수 있다.

## 👨‍🔬 실험 1: Closure를 활용해 게임 캐릭터에 스킬 적용하기
- Character 타입을 생성합니다.
`() → Void` 함수 타입을 갖는 `skill` 프로퍼티를 여러 개 만들어봅시다.
`() → Void` 함수 타입을 매개변수로 갖는 `tabShiftKey(skill: () → Void)` 함수를 만들어봅시다.
캐릭터를 생성하고, `skill`을 바꿔가며 `tabShiftKey` 메서드를 호출해봅시다.
![](https://hackmd.io/_uploads/rycjKbT_h.png)
![](https://hackmd.io/_uploads/SkU2YW6d2.png)
![](https://hackmd.io/_uploads/rJZTtZT_2.png)
![](https://hackmd.io/_uploads/SJYaFb6_3.png)

## 👨‍🔬 실험 2: 고차함수 활용해보기
- 원하는 형식의 배열을 생성합니다.
- 배열 안에 요소를 추가합니다.
- map, reduce, filter를 활용해보면서, 각 고차함수가 어떤 역할을 하는지 한마디로 표현해봅시다.

![](https://hackmd.io/_uploads/r1NzqZpu2.png)
![](https://hackmd.io/_uploads/rkqzq-pOn.png)
![](https://hackmd.io/_uploads/HkkQ5Zpu2.png)

## 👨‍🔬 실험 3: Custom 고차함수 직접 만들기
- map, reduce, filter의 각 기능을 떠올려보면서, 우리가 직접 같은 기능을 하는 고차 함수를 만들어보는건 어떨까요? 쉽진 않겠지만, 해낸 순간 느낄 무한한 뿌듯함은 보장합니다.
![](https://hackmd.io/_uploads/rJh5cbp_3.png)
![](https://hackmd.io/_uploads/HyzocWpu3.png)
![](https://hackmd.io/_uploads/B1Ys9bTd3.png)
![](https://hackmd.io/_uploads/HJAjqWa_2.png)


# 9주차: Table View
## 🔥실험 목표
- TableView를 생성할 수 있다.
- TableView의 Section과 Row의 개념을 이해하고, 원하는 정보를 Cell 에 표현할 수 있다.
![](https://hackmd.io/_uploads/H1VniBLK2.png)
- 섹션과 행으로 이루어져 유저에게 스크롤 가능한 리스트를 제공하고 선택할 수 있는 항목들을 표시하는 UI요소
![](https://hackmd.io/_uploads/HyFThr8F2.png)
- 테이블 뷰는 3가지 스타일을 지정할 수 있다.
    - plain
    - grouped
    - inset grouped

![](https://hackmd.io/_uploads/Sy72y8LY3.png)
- 필수 구현조건
    - UITableViewDataSource
    - UITableViewDelegate
    - UITableViewCell
![](https://hackmd.io/_uploads/HJUayL8Fn.png)
- 테이블 뷰에 표시되는 각 행을 나타내는 뷰 요소로 내부에는 셀 컨텐츠 부분과 액세서리 뷰부분으로 나눠진다.
- 액세서리 뷰는 필수 구현 사항은 아님
![](https://hackmd.io/_uploads/SkZyxULK2.png)
- 테이블뷰의 데이터관리를 하는 프로토콜. 
- 섹션과 행을 관리하고 각 행에 cell을 제공하는 역할을 한다.
![](https://hackmd.io/_uploads/Hyxel8IFn.png)
- 테이블 뷰의 동작 및 유저와의 상호작용에 대한 응답을 처리하는 프로토콜. 
- 셀이 터치되거나 슬라이드되거나할 때의 반응들을 정해줄 수 있다.
![](https://hackmd.io/_uploads/rkTTkL8F2.png)
![](https://hackmd.io/_uploads/rJNCy8UK2.png)
- indexPath는 2개의 인덱스를 가진 배열로 이루어져있다.
- 0번째 인덱스 = section, 1번째 인덱스 = row
![](https://hackmd.io/_uploads/H1mbl8LFn.png)
- 커스텀한 테이블 뷰를 만들기 위해서는 UITableViewDataSource와 UITableViewDelegate를 직접 구현해주는 편이 좋다.
![](https://hackmd.io/_uploads/S1YblIIYn.png)
![](https://hackmd.io/_uploads/rJxMeUIY2.png)
![](https://hackmd.io/_uploads/S1wuEIUK3.png)


# 10주차: Concyrrency Programing
![](https://hackmd.io/_uploads/H1SfJ-19h.png)
![](https://hackmd.io/_uploads/S1YXMkJc2.png)
![](https://hackmd.io/_uploads/SyMFfyk92.png)
- 컴퓨터에는 CPU라는 장치가 있다. 이는 컴퓨터에서 기억, 해석, 연산, 제어라는 4대 주요 기능을 관할하는 장치인데 그 중에서도 일을 처리하는 CPU의 핵심을 코어라고 한다. CPU와 코어는 동일한 것이 아니며, CPU는 여러 개의 코어를 가질 수 있다. 이는 하드웨어의 스레드라고도 볼 수 있는데, 스레드는 일을 처리하는 곳이기 때문. 소프트웨어의 스레드는 현실에 구현되어 있지는 않지만 논리적인 스레드로 컴퓨터에서 만들어내는 것들로 볼 수 있다. 앞으로 말하는 스레드는 전부 소프트웨어의 스레드이다.

![](https://hackmd.io/_uploads/SJ8C7J1qh.png)
- 병렬: 여러 개의 코어가 하나의 일을 나눠서 처리
- 동시성: 하나의 코어가 하나의 일을 여러 개의 스레드로 나눠서 처리

![](https://hackmd.io/_uploads/ByV-4y1cn.png)
- 동기: 작업이 끝날때까지 나머지는 모두 멈추고 기다림
- 비동기: 기다리지 않게 비켜 준 상태에서 작업.

![](https://hackmd.io/_uploads/H1z4N1Jq3.png)
- 스위프트에서 동시성 처리를 해주기 위해서는 이와 같은 것들이 있다.

![](https://hackmd.io/_uploads/BJUwVJ193.png)
- Grand Central Dispatch
- 코드로서 동기 / 비동기 처리만 해주면 알아서 스레드 관리

![](https://hackmd.io/_uploads/rJnvV1y9n.png)
- Dispatch 프레임워크, DispatchQueue 클래스 사용
- DispatchQueue라는 GCD를 사용하기 위한 대기열에 보내면 알아서 관리, 작업 처리
- 선입 선출로 작업 처리

![](https://hackmd.io/_uploads/r1GCHJkqn.png)
- 이러한 DispatchQueue를 사용하기 위해서 반드시 정해주어야 하는 조건이 있다.
- 단일 스레드인 Serial에서 처리할지
- 다중 스레드인 Concurrent에서 처리할지

![](https://hackmd.io/_uploads/Sk-J8J153.png)
- DispatchQueue.main과 DispatchQueue.global()은 기본적으로 구현되어 있는 큐이다.
- main은 Serial이며
- global()은 Concurrent이다.
- 여기서 sync 동기와 async 비동기를 선택해줄 수 있다.

![](https://hackmd.io/_uploads/HkxpIJkqn.png)
- 사용하면 이와 같다.
- main은 async라도 스레드가 하나밖에 없기에 global()의 sync일 때와 똑같이 동작한다.
- main에서는 sync를 사용할 수 없는데 이는 차차 알아보자.

![](https://hackmd.io/_uploads/HyqUvyk9n.png)
- workitem을 사용하여 클로저 처럼 코드블록을 캡슐화 해줄 수 있다. 
- excute 파라미터를 통해 사용.

![](https://hackmd.io/_uploads/r1ugOkk5h.png)
- 작업을 원하는 시간에 시작할 수 있게 하는 메서드들.
- deadline은 0초부터 시작해 5초가 되었을 때 시작하고
- walldeadline은 지금 당장의 시스템 시간에서 시작에 5초가 지난 시간에 시작.

![](https://hackmd.io/_uploads/SJXmKJyc3.png)
- 비동기로 되어 있는 특정 작업이 끝나기를 기다리게 한다.
- 위의 내용들이 defer와도 같다고 볼 수 있는지 생각해보자.
- 함수의 실행이 끝나면 실행되는 클로저인 escaping과도 유사하고
- 비동기 작업이 종료되는 시점을 추적하여 순서를 보장하는 completionHandler와도 유사해 보인다.

![](https://hackmd.io/_uploads/By-5h11c2.png)
- 앞서 나왔던 메인 스레드에 대한 이야기를 해보자.
- 메인 스레드는 기본 스레드로 여러가지 특징을 지닌다.
- 전역적으로 사용이 가능하고
- run loop 가 자동으로 설정되어 실행되는데 이때 run loop는 이벤트 처리 루프이다. 모든 스레드는 각자의 run loop를 가질 수 있지만 이는 자동적으로 설정되지는 않는다.run loop는 두 가지 이벤트를 처리하는데 하나는 Input Source로 다른 Thread나 Application으로부터 온 비동기 이벤트를 전달하는 것이고 다른 하나는 Timer라고 예약 된 시간 또는 반복 간격으로 발생하는 동기 이벤트를 전달하는 것.
- 마지막으로 UI 작업은 메인 스레드에서만 가능.

![](https://hackmd.io/_uploads/HyxO-lkqn.png)
- 앞서 메인 스레드에서는 sync를 부를 수 없는 이유도 이 특징들과 연관이 있다.
- sync의 특징으로 인해 메인 스레드는 sync가 끝날 때까지 스레드를 멈춘다.
- 그리고 sync는 코드 블록의 작업이 끝나기를 기다린다. (block wait)
- 그런데 메인 스레드가 멈추면서 메인 스레드에서 실행되고 있던 코드 블록도 멈춘다.
- DeadLock

![](https://hackmd.io/_uploads/By1tZl1q2.png)
- 그렇다면 UI 작업이 메인 스레드에서만 가능한 이유는 뭘까?

![](https://hackmd.io/_uploads/BJRyzgJqn.png)
- Race Condition이 일어날 수 있다면 Thread Unsafe 한다.

![](https://hackmd.io/_uploads/rJV-fg1c2.png)
- 마치 열쇠더미와 같다.

![](https://hackmd.io/_uploads/SkoVGe1ch.png)
- main과 global()은 기본 구현되어 있던 큐들이었는데 이를 convenience init 해주며 커스텀 해줄 수 있다.

![](https://hackmd.io/_uploads/SJQFzxkq3.png)
- label: 디버깅 시 구분

![](https://hackmd.io/_uploads/rktPOekq3.png)
- 무엇에 더 많은 에너지를 쏟을 지를 결정(더 많은 스레드를 할당할지) -> 순서를 결정 짓지는 않으나 영향 미칠 수 있음.
- User_interactive가 가장 높고 -> Unspecified가 가장 낮음
- 시스템이 알아서 조절

![](https://hackmd.io/_uploads/ryrrtxy53.png)
- serial: 단일
- concurrent: 다중
- initiallyinactive: activate()전까지 작동 x, 배열로 설정 받을 수 있음. [.concurrent, .initiallyInactive]

![](https://hackmd.io/_uploads/SJJhKgJc2.png)
- 디스패치큐가 작업을 스레드에 넘긴 후에 해당 작업을 큐에서 할당 해제하는 빈도
- inherit: 기본적인 ARC 참조 해제처럼 공간이 필요할 때마다 해제
- workitem: autoreleasePool과 같이 공간이 남아 있어도 매번 해제
- never: 해제 x

![](https://hackmd.io/_uploads/ByG65lJ5n.png)
- target 큐가 부모큐로 작업을 모조리 넘깁니다.
- 앱에서 사용하는 총 스레드 수를 최소화하기 위해 수행.
- 여러 개의 큐를 사용해야하는 작업들을 실제로는 하나의 큐에서 작업, 총 스레드 수 최소화.

![](https://hackmd.io/_uploads/BJoHjl1qh.png)
- async에는 사용할 수 있는 파라미터들이 있다. 
- 이들은 보통 기본값을 가지고 있어 구현해줄 필요는 없지만 필요시 수정하여 사용할 수 있다.
- 첫번째로 group이다.
- 서로 다른 큐에 있는 async 작업들도 그룹에 넣을 수 있음.
- 그룹의 작업들이 다 끝나면 다음 작업을 실행한다.
- sync는 작업 종료 시점을 알 필요가 없기에 async 작업에서만 사용 가능하다.

![](https://hackmd.io/_uploads/SJPV2e192.png)
- 두 가지 방법으로 등록 가능.
- 1. 파라미터
- 2. enter()부터 leave()까지 포함한다.

![](https://hackmd.io/_uploads/B1Lnng1cn.png)
- 1. notify: group이 끝나면 파라미터 queue에서 코드블럭 내용 실행
- 2. wait: 끝나기만 기다림. timeout으로 기다릴 시간도 정할 수 있음. wallTimeout도 사용 가능.

![](https://hackmd.io/_uploads/SkW4Tlk9h.png)
- 다음으로 qos와 flag라는 파라미터가 있다.
- qos: 앞서 설명한 DispatchQoS와 같다.
- flags: 코드 블럭 실행 시 적용할 추가 속성. 필요할 때 확인~

![](https://hackmd.io/_uploads/Sy3BAly5h.png)
![](https://hackmd.io/_uploads/SyQIRlJ52.png)
![](https://hackmd.io/_uploads/H1-vAl1c3.png)
![](https://hackmd.io/_uploads/rk5v0l15n.png)
![](https://hackmd.io/_uploads/rJG_Axy9n.png)
![](https://hackmd.io/_uploads/Sy8u0ek9n.png)
![](https://hackmd.io/_uploads/Hy3uAx1qh.png)




# 토요스터디 양식

🔥 불타는 토요스터디 9기 A반🔥 
📆 2023.05.27 토) Week #11
🫂 스터디원: Zion👑 , MINT😈 , yyss99🐣 , Yetti🌞 , redmango🐸 

[금주의 실험 주제] : 
    
    
# 토글 양식

<details>
<summary> 토글 양식 </summary>
<div markdown="1">

```swift=

```
        
</div>
</details>