Notice
Recent Posts
Recent Comments
Link
«   2025/07   »
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
Tags
more
Archives
Today
Total
관리 메뉴

iOS 개발일지

0805 새싹 25회차 정리 - Closures, DispatchQueue 본문

SeSAC iOS 데뷔과정 2기

0805 새싹 25회차 정리 - Closures, DispatchQueue

Lia's iOS 2022. 8. 5. 20:32
iOS 앱 개발자 데뷔 과정 25회차

 

 

 

 

#1. Checklist

- Closures

클로저는 익명함수, 이름 없는 함수를 의미한다.

클로저는 기본적으로 아래와 같이 '클로저 헤더' in 클로저 바디'의 형태이다.

매개변수가 생략될 경우, 할당되어 있는 내부상수 $0 을 사용할 수 있다. ($0, $1, $2 ...)

클로저의 기본 형태

 

 

Inline Closure : 함수의 매개변수 내에 클로저가 그대로 들어간 형태로, 코드를 생략하지 않고 클로저 구문을 작성한다.

Inline Closure

 

 

Trailing Cloure : 함수 뒤에 클로저가 실행되기 때문에 후행 클로저라고 한다.

Trailing Cloure

 

 

@autoclosure : 함수의 인자로 전달되는 코드를 감싸서 자동으로 클로저로 만들어 준다.

이때 사용되는 클로저는 인자가 없고 리턴값만 존재해야 한다.

@autoclosure를 사용하면 함수 parameter에 중괄호를 쓰는 것을 생략할 수 있게 해준다. 

assert() 함수를 예로 들어 이해해보려 한다.

 

assert

만약 파라미터 타입에 @autoclosure가 없다면, 호출할 때 아래와 같이 작성해야 할 것이다.

assert({ false }, { "Error Occurred!" })

 

하지만 @autoclosure가 붙어 있으므로, 중괄호를 생략하고 조금 더 간단하게 작성할 수 있다.

assert(false, "Error Occurred!")

 

 

 

Non-escaping : 클로저가 함수의 인자로 전달됐을 때, 함수의 실행이 종료되기 전에 실행되는 클로저이다.

기본적으로 클로저는 함수의 Scope 내에서만 동작하는 Non-Escaping 클로저로 사용된다.

직접 실행을 위해서만 사용되므로, 함수의 동작이 끝나면 이 클로저는 더이상 사용되지 않는다.

따라서 함수 내부라 할지라도 변수나 상수에 대입할 수 없다.

 

 

@escaping : 클로저가 함수의 인자로 전달됐을 때, 함수의 실행이 종료된 후 실행되는 클로저이다.

즉, Escaping 클로저는 함수 밖에서 실행되는 클로저이다.

Escaping 어노테이션 (@escaping) 으로 클로저를 넘기면 해당 함수가 종료되더라도 클로저를 사용할 수 있다.

예시를 통해 어떤 상황에 사용되는지 알아보자.

API request 함수

위의 코드에서 completionHandler Type은 Type Alias를 적용했으며, (Int, [String]) -> Void 타입이다.

서버 통신에 관한 코드를 ViewController 외부로 분리하는 작업을 하다 보니, VC와 데이터를 전달하는 코드가 필요해졌다.

그래서 전달받을 인자 (query, startPage) 와 전달할 인자 (completionHandler) 를 추가했더니 위와 같은 에러가 발생했다.

이 에러가 발생하는 이유는, completionHandler 클로저의 실행 시점이 '함수가 종료된 이후' 이기 때문이다.

completionHandler를 함수 밖에서 사용하려고 하는데 해당 함수는 Non-Escaping 함수이기 때문에 에러가 발생하는 것이다.

 

따라서 @escaping 을 클로저의 타입 앞에 붙여주면 에러는 사라진다.

 

 

 

- 고차함수 : filter, map, reduce

매개 변수로 함수를 받거나 반환값으로 반환할 수 있는 함수

반복문과 조건문 등으로 가능한 연산을 보다 간편하고 효율적으로 연산할 수 있다.

클로저 형태로 사용이 되며, Swift 표준 라이브러리에서 제공하는 대표적인 고차함수는 filter, map, reduce가 있다.

 

filte : 데이터를 추줄하고자 할 때 사용

매개변수로 전달된 함수를 실행하고, 특정 조건에 맞게 실행된 결과를 반환한다.

기존 컨테이너에서 내부의 값을 걸러, 새로운 컨테이너를 만든다고 할 수 있다.

이 필터를 이용해 기존의 코드를 개선해보면, 아래와 같이 깔끔해진다.

 

map : 데이터를 변형시키고자 할 때 사용 (새로운 데이터가 반환되며, 기존 데이터는 변경되지 않는다)

매개변수로 전달된 함수를 실행해 그 결과값을 다시 반환해주는 함수이다.

for-in 구문과 큰 차이가 없지만 코드가 간결해지고 재사용이 용이하며, 컴파일러 최적화 성능이 좋다.

 

reduce : 데이터를 합치고자 할 때 사용

기존 컨테이너에서 내부의 값들을 결합하여 새로운 값을 만든다.

사용하기 위해서는 초기값 지정이 필요한데, 초기값이 없을 경우에는 0으로 설정한다.

 

 

 

- Synchronous / Asynchronous

Synchronous (동기) : 한번에 하나의 작업만 진행

시작된 작업이 완료가 되어야 그 다음 작업을 시작한다.

작업 순서가 보장되기 때문에, 결과의 순서가 중요한 경우에는 동기 코드로 작성해야 한다.

 

Asychonous (비동기) : 한번에 여러 작업을 진행

시작한 작업이 아직 완료되지 않았더라도 다른 작업에 대해서 수행할 수 있다.

즉, 작업이 먼저 실행되었다 하더라도 먼저 끝나는 것은 아니기 때문에 작업 순서가 보장되지 않는다.

동기 코드가 모두 실행되고 난 후에 비동기 코드가 실행된다.

 

 

 

- Serial / Councurrent

Serial : Queue (대기열) 에 들어온 작업을 하나의 스레드에서만 분산하여 처리

Concurrent : Queue (대기열) 에 들어온 작업을 여러 스레드에 분산하여 처리

 

 

 

- DispatchQueue

Multi Thread : 하나의 스레드에서 이루어지던 작업을 다른 스레드에서도 작업할 수 있도록 분산처리 하는 방법

DispatchQueue를 활용하면 비교적 간단하게 구현 가능하며, 클로저를 통해서 작업 단위를 표현할 수 있다.

 

DispatchQueue는 global, main, custom의 세 종류가 있다.

DispatchQueue.global() : 작업 (Queue의 Task 항목) 을 여러 스레드로 나눠 보낸다 -> 동시 (Concurrent)

DispatchQueue.global() 의 경우 Concurrent Queue가 기본이다.

 

DispatchQueue.main : 작업을 여러 스레드로 분산하지 않고, 하나의 스레드 (메인 스레드) 에서 처리

UI에서 일어나는 작업은 global에서 작업하지 못하기 때문에, 메인 스레드에 구현해야 한다.

DispatchQueue.main 의 경우 Serial Queue가 기본이다.

 

DispatchQueue.global().async

예를 들어, 위 코드를 하나하나 뜯어서 쉽게 설명하면

DispatchQueue : (작업을 분배할) 매니저
global : 작업을 대기열에 있는 쓰레드에 분배할거야
async : 비동기로 해줘~

정도가 될 것 같다.

 

 

 

 

#2. Assignment

[5주차 마무리] 참고

 

- Class Singleton vs Struct Singleton

싱글톤 패턴을 구현할 때 구조체를 사용하지 않는 이유는?

 

싱글톤 패턴이란 특정 용도로 객체를 하나만 생성하여, 공용으로 사용하고 싶을 때 사용하는 디자인 유형이다.

Class에 자기 자신의 인스턴스를 static 프로퍼티로 생성하고, init 함수 접근제어자를 private로 만들어 다른 인스턴스 생성을 막는다.

여기서 중요한 것은 값을 공용으로 쉽게 접근하고 사용하는 것이 목적이므로, 동일한 주소 값을 가져야 한다.

 

하지만 우리가 알고 있듯이 Struct는 값 타입이기 때문에, 외부에서 인스턴스의 값을 변경해도 Struct 내부의 값은 변경되지 않는다.

이처럼 값을 공유할 수 없기 때문에 구조체의 싱글톤 패턴은 모순적이라고 할 수 있다.

 

 

 

 

# 그 외

Swift 참고 문서 : https://jusung.gitbook.io/the-swift-language-guide/language-guide/23-automatic-reference-counting