210504 Tue

Sunny·2021년 5월 8일
0

Today I Learned

목록 보기
45/88

학습 내용

  • 초기값 initializer ⭐️ → 각자 공부해와서 스터디 형식으로 설명해보는 시간 갖기! (at 2pm)

    써니: 꼼꼼한 재은씨 문법편

    이안: 야곰 책 → 이안은 내일 설명해보는 걸루!

    정리할 것 (오늘 하루종일 해도 부족할듯... ^.ㅠ)

    • Operation Queue에서의 우선순위와 다중처리 방법 ⭐️ (at 7pm)

    • 고객 타입을 따로 만들고 프로퍼티로 번호표를 갖고 있는건 어떨까요?

      순번표를 갖고 있는게 고객의 역할일 수 있을 것 같아요.

    • 고객 대기열 → 배열 구현 방법 생각해오기

    • 오퍼레이션 상속

      은행원의 업무를 Operation을 상속 받아 만들어 보는 건 어떨까요? 고객을 전달받아서 번호표를 확인 후 업무를 시작하면 몇 번째의 은행원이 업무를 마쳤는지 확인할 수 있을 것 같아요. 창구의 고유 번호는 몰라도 될것 같은게, 여러명의 은행원들 중 놀고있는 첫 번째 은행원에게 고객을 할당하면 되지 않을까요?

    • 비동기적으로 Operation이 끝난 후 호출되는 closure도 활용 방법 찾아보기

    • Step2용 플로우차트, UML 다시 그려야 !

    • 익스텐션, 프로토콜 어케 활용하지? 🤔

1. 첫 번째 학습 내용: 구조체 VS 클래스/ 인스턴스/ 초기화 개념 복습

인스턴스

우리가 정의한 구조체나 클래스를 그대로 사용해서 값을 저장하거나 메소드를 실행할 수는 없음
Why? 이들 자체는 단순히 객체의 정의일 뿐, 실제로 값을 저장하고 메소드를 호출하는 데에 필요한 메모리 공간을 할당받지 못했기 때문

타입(설계도 = 블루프린트)을 바탕으로 실질적인 값을 저장하고 사용하려면 메모리 공간을 할당받은 객체가 필요함!

구조체나 클래스는 실행할 수 있는 객체가 아니라, 값을 담을 수 있는 실질적인 그릇을 만들어내기 위한 일종의 틀로 생각해야 함. 즉, 원형(Origin)이라는 것!

틀 역할을 하는 클래스나 구조체를 정의하고
이를 바탕으로 실질적으로 값을 담을 여러 개의 그릇을 만들어내는 것,
이것이 객체지향 프로그래밍의 원리

이때 원형 틀을 이용하여 찍어낸 그릇을 인스턴스(Instance)라고 함
= 타입의 설계도를 사용하여 메모리 공간을 할당받은 것이 인스턴스
우리가 실질적인 값을 담을 수 있는 것은 바로 이 인스턴스임

초기화

스위프트에서 옵셔널 타입으로 선언되지 않은 모든 프로퍼티는 명시적으로 초기화해 주어야 함. 초기화되지 않은 프로퍼티가 있을 경우 컴파일러는 이를 컴파일 오류로 처리함 (→ 앱이 빌드되지 못하는 것)

명시적인 초기화 방법
1. 프로퍼티를 선언하면서 동시에 초기값을 지정하는 경우
2. 인스턴스 생성 과정(= 초기화 메소드 내)에서 프로퍼티의 초기값을 지정하는 경우
⇒ 클래스나 구조체의 모든 프로퍼티는 적어도 인스턴스가 생성되는 시점까지는 반드시 초기화되어야 한다.

구조체는 모든 프로퍼티의 값을 인자값으로 입력받아 초기화하는 기본 초기화 구문을 자동으로 제공함 → 이 초기화 구문을 Memberwise Initializer라고 부르기도 함

import Foundation

struct Resolution {
    var width = 0
    var height = 0
    
    func desc() -> String {
        return "Resolution 구조체"
    }
}

// width와 height를 매개변수로 하여 Resolution 인스턴스를 생성
// 멤버와이즈 초기화 구문(Memberwise Initializer)
let defaultRes = Resolution(width: 1024, height: 768)
print("width:\(defaultRes.width), height:\(defaultRes.height)") // print width:1024, height:768

구조체의 인스턴스를 생성할 때 사용할 수 있는 초기화 구문

  1. Resolution() // 기본 초기화 구문. 내부적으로는 프로퍼티를 초기화하지 않음
  2. Resolution(width: Int, height: Int) // 모든 프로퍼티의 초기값을 입력받는 멤버와이즈 초기화 구문. 내부적으로 모든 프로퍼티를 초기화함

저장 프로퍼티 (Stored property)

= 클래스 내에서 선언된 변수나 상수를 부르는 이름

저장 프로퍼티를 선언할 때에도 초기값을 할당할 수 있음
하지만, 반드시 선언하는 시점에서 초기값을 할당해야 하는 것은 아님
Why? 초기화 구문에서 초기값을 설정해도 됨
(e.g. 구조체의 멤버 와이즈 구문이 이같은 역할)
= 이런 멤버와이즈 구문이 프로퍼티 값의 초기화를 보장해주기 때문에 옵셔널 타입으로 지정하지 않아도 됨

But 클래스에서 프로퍼티를 선언할 때 초기값을 함께 할당해 주지 않으면 주의가 필요!

  • 프로퍼티 선언 시 초기값이 할당되지 않은 저장 프로퍼티는 반드시 옵셔널 타입으로 선언해 주어야 함
  • 스위프트에서는 클래스의 프로퍼티에 값이 비어있으면 인스턴스를 생성할 때 무조건 nil 값으로 초기화하기 때문
  • 물론 초기값을 처음부터 할당해 준다면 옵셔널 타입으로 선언할 필요 없음
class User {
    var name: String
}

Error message: Class 'User' has no initializers
Stored property 'name' without initial value prevents synthesized initializers

Solution 1: 초기화 구문을 작성하고, 그 안에서 초기값을 할당해 준다

class User {
    var name: String

    init() {
        self.name = ""
    }
}

Solution 2: 프로퍼티를 옵셔널 타입으로 바꿔줌

class User {
    var name: String?
}

Solution 3: 프로퍼티에 초기값을 할당해 줌

class User {
    var name: String = ""
}

init 초기화 메소드

import Foundation

struct Resolution {
    var width = 0
    var height = 0

    // 초기화 메소드 : Width를 인자값으로 받음
    init(width: Int) {
        self.width = width
    }
}

class VideoMode {

    var resolution = Resolution(width: 2048)
    var interlaced = false
    var frameRate = 0.0
    var name: String?

    // 초기화 메소드: interlaced, frameRate 두 개의 인자값을 받음
    init(interlaced: Bool, frameRate: Double) {
        self.interlaced = interlaced
        self.frameRate = frameRate
    }
}

// Resolution 구조체에 대한 인스턴스를 생성
let resolution = Resolution(width: 4096)

// VideoMode 클래스에 대한 인스턴스를 생성
let videoMode = VideoMode(interlaced: true, frameRate: 40.0)

인스턴스 생성 구문은 단지 생략되어 있을 뿐 init 초기화 메소드를 호출하는 것
so 초기화 메소드에 정의된 매개변수 및 인자값 타입과 일치하는 형식으로만 사용할 수 있음
(e.g. Resolution 구조체의 초기화 메소드에는 width 매개변수가 정의되어 있으므로 인스턴스를 생성할 때에도 width 매개변수의 인자값을 넣어줘야 함)

class VideoMode {

    var resolution = Resolution(width: 4096)
    var interlaced = false
    var frameRate = 0.0
    var name: String?
    
    // 기본 초기화 구문
    init() {
    }

    // 초기화될 때 name 인자값만 받는 init 구문
    init(name: String) {
        self.name = name
    }
}

// VideoMode 클래스에 대한 인스턴스를 생성
let defaultVideoMode = VideoMode()
let nameVideoMode = VideoMode(name: "Sunny")
class VideoMode {

    var name: String?

    // 초기화될 때 name 인자값을 받는 init 구문
    init(name: String = "") {
        self.name = name
    }
}

// VideoMode 클래스에 대한 인스턴스를 생성하고 상수에 할당
let defaultVideoMode = VideoMode()
let nameVideoMode = VideoMode(name: "Sunny")

2. 두 번째 학습 내용: Operation Queue에서의 Priority levels/ Dependencies

Customizing the Execution Behavior of an Operation Object

중요: operation 객체를 생성하고 나서

이 아이들을 queue에 집어넣기 전에

configure 해줘야 한다.

The configuration of operation objects occurs after you have created them but before you add them to a queue.

Changing an Operation’s Execution Priority

Instance Property queuePriority

The execution priority of the operation in an operation queue.

var queuePriority: Operation.QueuePriority { get set }

Priority level은 오직 같은 오퍼레이션 큐에 있는 작업들에만 해당됨.
만약에 오퍼레이션 큐가 여러개 있고 & 우선순위 다른 작업들이 서로 흩어져 있으면 → 우선순위 낮은 작업이 (다른 큐에 있는 우선 순위 높은 작업들보다) 먼저 실행될 수도 있음.

Priority levels apply only to operations in the same operation queue. If your application has multiple operation queues, each prioritizes its own operations independently of any other queues. Thus, it is still possible for low-priority operations to execute before high-priority operations in a different queue.

queuePriority는 enum 타입임 !!
Enumeration Operation.QueuePriority
Constants로 각 케이스들 veryLow부터 veryHigh까지 5가지 경우가 나뉘어져 있음
그리고 그 케이스들을 클릭해서 들어가보면
case veryHigh = 8
case veryLow = -8
이런 식으로 애플에서 이미 값을 지정해줌!!

이안) 만약 경우의 수가 5개가 넘어가면 어떻게 할까 ?
그럴 때는 우리가 직접 위에 케이스처럼 숫자를 넣어줘서 우선 순위를 설정해주면 됨 !!

QueuePriority

동일한 큐에 추가되어 있는 오퍼레이션 사이의 상대적인 우선 순위를 설정할 수 있습니다. 높은 우선 순위를 가진 오퍼레이션이 먼저 실행됩니다. veryHigh부터 veryLow까지 (veryHigh, high, normal, low, veryLow) 설정가능하며 직접 설정하지 않으면 normal로 설정됩니다.

[iOS] Operation? Operation Queue? 작업을 객체로 만들어보자!

스티븐) 은행원 3명
반복문 돌면서 addOperation
오퍼레이션이 큐 안에 들어가면
QueuePriority로 정렬을 해주는 듯

1,2,3 중에 일반이나 VIP가 있을때
오퍼레이션 큐에 들어가자마자 스레드로 넘어감

그래서 배열을 통째로 넣어줌 !!!

스티븐의 팁 👏
func addOperations([Operation], waitUntilFinished: Bool)
Adds the specified operations to the queue.

고객 객체
고객 객체안에 테스크 객체
테스크 객체가 오퍼레이션을 상속받음

Dependency

If you want to prevent one operation from starting until another operation has finished, you must use dependencies
VVIP와 VIP 고객 업무를 마치기 전에,
일반 고객 작업이 시작되는 걸 막으려면
dependencies를 활용해야 하지 않을까?

Configuring Interoperation Dependencies

This dependency means that the current object cannot begin executing until the target object finishes executing.

프로젝트를 예로 들면
the current object (일반 고객) → the target object (VIP 고객)
일반 고객 operation은 execute 실행을 시작하면 안됨
until VIP 고객 업무가 완료되기 전까지!

Priority level과 달리 의존성은 꼭 같은 queue에 있는 operation에만 해당되는게 아님!
= operation 간에 dependencies를 만들고
이 작업할 아이들을 서로 다른 queue간에 넣어도 상관이 없다는 말씀!

Dependencies are also not limited to operations in the same queue. Operation objects manage their own dependencies and so it is perfectly acceptable to create dependencies between operations and add them all to different queues.

Operation Dependencies

Dependencies are a convenient way to execute operations in a specific order. You can add and remove dependencies for an operation using the addDependency: and removeDependency: methods.

참고 자료

Concurrency Programming Guide - Operation Queues

Instance Property - dependencies

3. 세 번째 학습 내용: completionBlock

비동기적으로 Operation이 끝난 후 호출되는 closure도 활용 방법 찾아보기
→ 하이디가 말한게 혹시 completionBlock 활용하라는 의미인걸까 ?? (to 하이디)

A. 하이디 답변

"비동기적으로 Operation이 끝난 후 호출되는 closure 활용"은 범용적인 의미에서 말한거였는데요~ iOS의 비동기 프로그래밍에서 작업이 끝난 후에 함수롤 호출해주는 방식이 자주 사용되므로 이런 방식에 익숙해지기 위해 연습해보면 좋다라는거였어용~~
저 completionBlock을 얘기한 것은 아니었지만, 제가 말씀드렸던 방식과 같은것 같네요! 요구사항 구현에 적합하다면 활용해 봐도 좋을 것 같아요

Instance Property

completionBlock

The block to execute after the operation’s main task is completed.

Setting Up a Completion Block

In OS X v10.6 and later, an operation can execute a completion block when its main task finishes executing. You can use a completion block to perform any work that you do not consider part of the main task. For example, you might use this block to notify interested clients that the operation itself has completed. A concurrent operation object might use this block to generate its final KVO notifications.

To set a completion block, use the setCompletionBlock method of NSOperation. The block you pass to this method should have no arguments and no return value.

고민하는 점 🤔

  • 어차피 Queue는 하나면 되지 않나 ??
  • Queue를 하나만 쓸거니까 QueuePriority를 활용하기로 함 !

고객 타입 만들어서 3개 갖고 생성

대기번호,
업무 타입,
등급 = VIP

→ 큐에 보내면
큐가 스레드에 보냄
(스레드에 보내기 전에 배열을 이용해서 정렬)

고객을 생성할 때 ??
인스턴스를 오늘 고객 수 만큼 계속 생성 !

랜덤 번호를 생성해서
그 숫자를 고객 수 변수에다가 넣고
그만큼 print문 찍기

스티븐은 은행 업무라는 클래스를 만들고 이게 오퍼레이션을 상속받게 만듦 !!

Thanks to 스티븐 👍

  • 우선순위를 위해서 queuePriority 혹은 dependency 를 쓰는건지?
    둘다 써야 하는건지? 어떻게?

  • 고객 타입 만들고, 프로퍼티로 번호표 만들고, 랜덤 고객 수 만큼 고객 인스턴스를 만들어줘야 하나?
    → 그 고객들을 배열에 넣어줄때 번호표를 기준으로 정렬??
    → 번호표를 큐에 옮기나??

    번호표 일단 신경 안쓰기로 했음
    일단 고객 등급에 집중하는 걸로 !

기타)

  • 익스텐션이랑 프로토콜은 이걸 써야지 구현이 되는건지?
    아님 refactoring 차원에서 활용을 하는건지? 🤔 (to Cozy)

맨날 개념 공부만 실컷 하고
그래서 코드에 어떻게 해야 하는지는 모르겠다 헤 😢

찾아보쟈...⭐️

profile
iOS Developer

0개의 댓글