├── README.md
├── Swift
├── AccessControl.md
├── CaptureList.md
├── Closure.md
├── Defer.md
├── Delegate,Notification,KVO.md
├── Equatable,Comparable,Hashable.md
├── GCD.md
├── HigherOrderFunction.md
├── Initialization.md
├── MemoryReference.md
├── NestedFunctions.md
├── Optional.md
├── map들.md
└── 특징.md
├── iOS
├── App&SceneDelegate.md
├── CoreData-migration.md
├── ImageDownload:Cache.md
├── MainRunLoop.md
├── RenderingProcess.md
├── TableView.md
├── viewLifeCycle.md
└── 오토레이아웃과 오토리사이징.md
└── 개발 상식
├── http메시지.md
└── 프레임워크와라이브러리.md
/README.md:
--------------------------------------------------------------------------------
1 | # iOS-Interview-KR
2 |
3 | ## Swift
4 |
5 | * [Swift 언어의 특징](https://github.com/Sueaty/iOS-Interview-KR/blob/main/Swift/특징.md)
6 | * [Optional](https://github.com/Sueaty/iOS-Interview-KR/blob/main/Swift/Optional.md)
7 | * [Access Control(접근제어)](https://github.com/Sueaty/iOS-Interview-KR/blob/main/Swift/AccessControl.md)
8 | * [Defer](https://github.com/Sueaty/iOS-Interview-KR/blob/main/Swift/Defer.md)
9 | * [CaptureList](https://github.com/Sueaty/iOS-Interview-KR/blob/main/Swift/CaptureList.md)
10 | * [MemoryReference](https://github.com/Sueaty/iOS-Interview-KR/blob/main/iOS/MemoryReference.md)
11 | * [map, flatMap, compactMap](https://github.com/Sueaty/iOS-Interview-KR/blob/main/Swift/map%EB%93%A4.md)
12 | * [GCD](https://github.com/Sueaty/iOS-Interview-KR/blob/main/Swift/GCD.md)
13 | * [Initialization](https://github.com/Sueaty/iOS-Interview-KR/blob/main/Swift/Initialization.md)
14 | * [Nested Functions](https://github.com/Sueaty/iOS-Interview-KR/blob/main/Swift/NestedFunctions.md)
15 | * [Closure](https://github.com/Sueaty/iOS-Interview-KR/blob/main/Swift/Closure.md)
16 | * [Delegate, Notification, KVO](https://github.com/Sueaty/iOS-Interview-KR/blob/main/Swift/Delegate%2CNotification%2CKVO.md)
17 |
18 | ## iOS
19 |
20 | * [AutoLayout 코드로 작성하는 3가지 방법](https://sueaty.tistory.com/174)
21 | * [Leading Constraint? Left Constraint?](https://sueaty.tistory.com/175)
22 | * [Intrinsic content size, Content Hugging, Compression Resistance](https://sueaty.tistory.com/176)
23 | * [View Controller의 Life cycle](https://sueaty.tistory.com/178)
24 | * [URLSession](https://sueaty.tistory.com/181)
25 | * [Frame vs Bounds](https://sueaty.tistory.com/182?category=867436)
26 |
27 | * [Core Data](https://yeonduing.tistory.com/65)
28 | * [Core Data - 다른 영구저장소와의 비교](https://yeonduing.tistory.com/54)
29 | * [오토 레이아웃과 오토 리사이징](https://github.com/Sueaty/iOS-Interview-KR/blob/main/iOS/%EC%98%A4%ED%86%A0%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83%EA%B3%BC%20%EC%98%A4%ED%86%A0%EB%A6%AC%EC%82%AC%EC%9D%B4%EC%A7%95.md)
30 | * [TableView](https://github.com/Sueaty/iOS-Interview-KR/blob/main/iOS/TableView.md)
31 | * [View LifeCycle](https://github.com/Sueaty/iOS-Interview-KR/blob/main/iOS/viewLifeCycle.md)
32 | * [RunLoop](https://github.com/Sueaty/iOS-Interview-KR/blob/main/iOS/RunLoop.md)
33 | * [AppDelegate & SceneDelegate](https://github.com/Sueaty/iOS-Interview-KR/blob/main/iOS/App&SceneDelegate.md)
34 | * [Scene-Based Life-Cycle](https://yeonduing.tistory.com/63)
35 | * [Rendering Process](https://github.com/Sueaty/iOS-Interview-KR/blob/main/iOS/RenderingProcess.md)
36 | * [RxSwift - Observable](https://yeonduing.tistory.com/60)
37 | * [RxSwift - PublishSubject](https://yeonduing.tistory.com/61)
38 | * [RxSwift - just, of, from](https://yeonduing.tistory.com/62)
39 | * [Combine - 시작하기에 앞서](https://sueaty.tistory.com/140) && [Combine 기초](https://sueaty.tistory.com/141)
40 | * [Combine - Publisher, Subscriber ❶](https://sueaty.tistory.com/142)
41 | * [UIResponder](https://yeonduing.tistory.com/66)
42 |
43 | ## CS
44 | ### Database
45 | * [NoSQL이란](https://sueaty.tistory.com/137)
46 | ### OS
47 | * [프로세스와 스레드](https://yeonduing.tistory.com/59)
48 | ### 네트워크
49 | * [http메시지](https://github.com/Sueaty/iOS-Interview-KR/blob/main/%EA%B0%9C%EB%B0%9C%20%EC%83%81%EC%8B%9D/http%EB%A9%94%EC%8B%9C%EC%A7%80.md)
50 |
--------------------------------------------------------------------------------
/Swift/AccessControl.md:
--------------------------------------------------------------------------------
1 | ## Optional과 관련된 면접 질문
2 |
3 | * 접근 제어자의 종류엔 어떤게 있는지 설명하시오 [출처](https://github.com/JeaSungLEE/iOSInterviewquestions#swift)
4 |
5 | # Access Control
6 |
7 | Access control은 **파일 또는 모듈 내의 코드에 접근을 제한할 수 있는 방법**이다.
8 | 구현한 코드에 접근 제한을 두면 개발자로 하여금 내가 의도한대로 코드를 작성하도록 유도할 수 있다.
9 |
10 | OOP 패러다임에서 **Encapsulation**(캡슐화), **Hiding**(은닉화)이 중요한 이유는 외부로부터 데이터/코드가 보호되어야 하기 때문이다.
11 | 외부 접근으로 인해 의도치 않은 결과가 초래될 수 있으므로 적절히 접근제어를 통해 제한할 필요가 있다.
12 |
13 | Access control은 다음에 지정할 수 있다.
14 | * Type: class, struct, enumeration
15 | * (Type 내의) properties, methods, initializers, subscripts (단, enumeration의 case마다 접근 지정자는 붙이지 못함)
16 |
17 | ## Access Level
18 |
19 | Access level keyword를 통해 access control 을 조절할 수 있는데 Swift에는 총 5가지의 access level이 존재한다.
20 | `open`, `public`, `internal`, `fileprivate`, `private`
21 | 위 순서는 접근도를 높은 순서대로 나열한 것이며, 모든 타입에 적용되는 access level은 **상위 요소보다 하위 요소가 더 높은 접근 수준을 가질 수 없다**.
22 |
23 | ### open
24 |
25 | * 외부 모듈에서 접근 가능
26 |
27 | | | open | 그 외의 access level |
28 | | ---- | -------- | -------- |
29 | | 클래스 상속 | 제한 없음 | 해당 클래스가 정의된 모듈 내 |
30 | | 클래스 멤버 재정의 | 제한 없음 | 해당 클래스 멤버가 정의된 모듈 내 |
31 |
32 | ### public
33 |
34 | * 외부 모듈에서 접근 가능
35 |
36 | ### internal
37 |
38 | * default로 지정되는 access level
39 | * 소스 파일이 속해있는 모듈 내에서 사용 가능(= 해당 모듈을 외부에서 import 했다면 접근 불가)
40 |
41 | ### fileprivate
42 |
43 | * 요소가 구현된 소스파일 내부에서만 사용 가능
44 | * 해당 소스파일 외부에서 값이 변경되거나 함수를 호출하면 부작용이 생길 수 있을 때 사용하면 좋음
45 |
46 | ### private
47 |
48 | * 그 기능을 정의하고 구현한 범위 내에서만 사용 가능
49 | * 같은 소스파일 안에 구현되어 있더라도 구현한 타입이 다르면 사용 불가
50 |
51 | ```swift
52 | // 파일명 : example.swift
53 | class A {
54 | fileprivate var fpProperty: Int = 3
55 | private var pProperty: Int = 5
56 | }
57 |
58 | class B {
59 | let a = A()
60 |
61 | func printAProperties() {
62 | print(a.fpProperty) // 가능
63 | print(a.pProperty) // 불가능
64 | }
65 | }
66 | ```
67 |
--------------------------------------------------------------------------------
/Swift/CaptureList.md:
--------------------------------------------------------------------------------
1 | ## Capture List
2 |
3 | 기본적으로 클로저는 주변 범위에서 변수와 상수를 strong reference로 캡쳐한다. capture list를 사용하여 클로저에서 값이 캡처되는 방식을 명시적으로 제어할 수 있다. capture list를 사용할 때는 파라미터 이름, 타입, 반환 타입을 제외하더라도 `in` 키워드를 반드시 써야한다.
4 |
5 | 캡처 리스트는 클로저가 **생성될 때 초기화** 된다. 각 항목은 주변 범위에서 이름이 같은 상수 혹은 변수로 초기화 된다.
6 |
7 | ```swift
8 | var a = 0
9 | var b = 0
10 | let closure = { [a] in
11 | print(a, b)
12 | }
13 |
14 | a = 10
15 | b = 10
16 | closure()
17 | // Prints "0 10"
18 | ```
19 |
20 | 위 코드에서 a는 클로저 주변의 변수와 클로저 내부의 상수가 있다. 클로저 내부의 a는 클로저가 생성될 때 초기화 된다. 그러나 연결은 되어있지 않기 때문에 클로저를 실행 했을 때, 처음 캡처한 0값이 출력된다. 그러나 b 변수는 하나이기 때문에 변화가 모두 적용된다.
21 |
22 |
23 |
24 | +) 내용 추가
25 |
26 | 위 코드의 b를 보면 알 수 있듯이 클로저에서 명시적 capture list를 작성하지 않으면 value type 변수일지라도 **reference capture**가 일어난다. (그래서 b 값이 10으로 출력됨) 따라서 value copy를 위해서는 [b] 와 같이 captrue list를 명시해 주어야 한다.
27 |
28 | 또한 value capture된 값은 closure 안에서 변경이 불가능하다. b를 value capture 하지 않은 상태에서 closure 내부에서 변경하게 되면 reference capture이기 때문에 기존 b 역시 변경된다.
29 |
30 |
31 |
32 |
33 |
34 | ```swift
35 | class SimpleClass {
36 | var value: Int = 0
37 | }
38 | var x = SimpleClass()
39 | var y = SimpleClass()
40 | let closure = { [x] in
41 | print(x.value, y.value)
42 | }
43 |
44 | x.value = 10
45 | y.value = 10
46 | closure()
47 | // Prints "10 10"
48 | ```
49 |
50 | 그러나 이러한 차이는 참조 의미론을 가진 변수에서는 적용되지 않는다. 그리고 표현 값의 타입이 class면 weak나 unowned로 값의 참조를 명시할 수 있다.
51 |
52 | ```swift
53 | myFunction { [weak parent = self.parent] in print(parent!.title) }
54 | ```
55 |
56 | 또한 임의의 식을 capture list에서 명명된 값에 바인딩 할 수도 있다.
--------------------------------------------------------------------------------
/Swift/Closure.md:
--------------------------------------------------------------------------------
1 | ## Closure
2 |
3 | https://docs.swift.org/swift-book/LanguageGuide/Closures.html
4 |
5 | 클로저는 코드 블럭. 일급시민으로 전달인자, 변수, 상수 등으로 저장 및 전달이 가능하다. 함수는 클로저의 일종으로 이름이 있는 클로저라고 생각하면 된다.
6 |
7 | 값으로써 함수를 인수로 지정하고, 다른 함수에 전달하여 사용하는 것을 클로저 라고한다. Swift의 클로저는 C 나 Objective-C에서의 blocks와 유사하고, 다른 프로그래밍 언어의 lambdas와 비슷하다.
8 |
9 | 클로저는 어떤 상수나 변수의 참조를 캡쳐해 저장할 수 있다. 이 캡쳐와 관련된 모든 메모리 관리는 Swift가 알아서 처리한다.
10 |
11 | 전역 함수나 중첩 함수는 클로저의 특별한 케이스라고 볼수 있다. 클로저는 다음 세 가지 형태 중 하나로 나타난다.
12 |
13 | - **전역 함수**: 이름을 가지고 있고, 값을 캡쳐하지 않는 클로저
14 | - **중첩 함수**: 이름을 가지고 있고, 그들의 폐쇄함수(감싸고 있는 함수)에 의해 값이 캡쳐될 수 있음
15 | - **클로저 표현**: 이름을 가지고 있지 않고, 주변 컨텍스트로부터 값을 캡쳐할 수 있는 간략화된 구문
16 |
17 | Swift의 클로저 표현은 깔끔하고, 명확한 스타일로, 일반적 상황에서 간단하면서도 군더더기 없는 최적화 기능을 제공한다. 다음은 최적화의 일종이다.
18 |
19 | - 인자와 반환 값의 타입을 추론
20 | - 단일 표현식 클로저로부터 암시적 반환
21 | - 축약된 인자 이름
22 | - 후행 클로저
23 |
24 | ## Capturing Values
25 |
26 | ```swift
27 | func makeIncrementer(forIncrement amount: Int) -> () -> Int {
28 | var runningTotal = 0
29 | func incrementer() -> Int {
30 | runningTotal += amount
31 | return runningTotal
32 | }
33 | return incrementer
34 | }
35 |
36 | let incrementByTen = makeIncrementer(forIncrement: 10)
37 |
38 | print(incrementByTen()) // 10
39 | print(incrementByTen()) // 20
40 | print(incrementByTen()) // 30
41 | ```
42 |
43 | **다음과 같은 nested function 에서 incrementByTen 함수가 각각 실행되지만, `runningTotal` , `amount` 가 캡쳐링 되어 계산이 누적된 결과를 갖게 된다.**
44 |
45 | ### Autoclosures
46 |
47 | 인자 값이 없으며, 특정 표현을 감싸서 다른 함수에 전달인자로 사용할 수 있는 클로저. 자동클로저는 클로저를 실행하기 전까지 실제 실행이 되지 않는다.
48 |
49 | ```swift
50 | var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
51 | print(customersInLine.count)
52 |
53 | // 자동 클로저 (인자가 없고, 특정 표현을 감싸서 다른 함수에 인자로 사용할 수 있음)
54 | let customerProvider = { customersInLine.remove(at: 0) }
55 | print(customersInLine.count)
56 |
57 | // 이 때 지연 실행된다
58 | print("Now serving \\(customerProvider())!")
59 | print(customersInLine.count)
60 |
61 | func serve(customer customerProvider: () -> String) {
62 | print("Now serving \\(customerProvider())")
63 | }
64 | serve(customer: { customersInLine.remove(at: 0) })
65 |
66 | // @autoclosure 키워드 붙이기
67 | func serve(customer customerProvider: @autoclosure () -> String) {
68 | print("Now serving \\(customerProvider())")
69 | }
70 |
71 | // 클로저 형식({})으로 넣지 않아도 됨
72 | serve(customer: customersInLine.remove(at: 0) )
73 | ```
--------------------------------------------------------------------------------
/Swift/Defer.md:
--------------------------------------------------------------------------------
1 | ## defer 관련 면접 질문
2 |
3 | * defer란 무엇인지 설명하시오. [출처](https://github.com/JeaSungLEE/iOSInterviewquestions)
4 | * defer가 호출되는 순서는 어떻게 되고, defer가 호출되지 않는 경우를 설명하시오. [출처](https://github.com/JeaSungLEE/iOSInterviewquestions)
5 |
6 | # defer
7 |
8 | ## defer 란
9 |
10 | defer 구문이 하는 역할에 대한 궁금증은 단어 자체의 뜻을 알면 많은 부분 해소된다.
11 |
12 | 
13 |
14 | > A defer statement is used for executing code **just before transferring program control**
15 | > outside of the scope that the defer statement appears in.
16 |
17 | defer 구문은 자신이 속해있는 **코드 블럭의 scope에서 빠져나가기 '직전'** 에 실행되는 구문이다.
18 | 이 때 scope는 함수, 조건문, 반복문, do-try-catch문 등의 scope을 모두 뜻하고 빠져나간다는 것은 정상 종료를 뜻할수도 있고 에러를 만나 비정상 종료가 될 수도 있다는 뜻이다.
19 | 하지만 **어떤 종료를 하던 무관하게 빠져나가기 직전에 defer문 내부의 코드를 실행하고 종료** 한다.
20 |
21 | 어떤 형태로 빠져나가도 반드시 실행이 보장되어 있는 구문이라서 오류처리를 할 때 유용하게 쓰인다.
22 |
23 | ## defer 사용 예시
24 |
25 | ### 파일 handling
26 |
27 | 코드 설명 : Network의 상태 메세지를 가져와서 정상("Success")이 아닐 경우 상태 메세지를 파일에 write
28 |
29 | ```swift
30 | func logErrorMessage() {
31 | let file = openFile(...)
32 | defer { file.close() }
33 |
34 | guard let statusMessage = fetchNetworkStatus() where statusMessage != "Success" {
35 | return
36 | }
37 |
38 | file.write(statusMessage)
39 |
40 | }
41 | ```
42 |
43 | 파일에 읽고 쓰는 작업을 하려면 파일을 열어야하고, 종료 시 안전하게 닫아야 한다.
44 | 파일을 닫는 코드가 defer block 안에 있기 때문에 해당 함수가 종료되기 직전에 실행이 되는데, 불리는 시점은 else 문이 종료될 때 일수도 있고, 정상 실행 후 마지막에 종료 직전에 불릴 수 있다. 하지만 어떤 종료를 하건 무조건 파일은 닫아야 하니 defer block안에 넣은 것이다.
45 |
46 | ## defer의 특징
47 |
48 | 코드 설명 : do-try-catch 문에서 무조건 catch 문으로 넘어가게 함
49 |
50 | ```swift
51 | do {
52 | defer { print("1") }
53 | defer { print("2") }
54 | try fail()
55 | print("do-statement finished")
56 | } catch {
57 | print("catch statement")
58 | }
59 |
60 | // 무조건 Error가 발생
61 | func fail() throws -> () {
62 | throw NSError(domain: "U", code: 1, userInfo: nil)
63 | }
64 | ```
65 |
66 | fail 함수를 실행 위쪽을 보면 2개의 defer block이 있다.
67 | 즉, try fail()을 통해 error가 발생한다면 catch문으로 넘어갈텐데, 그 전에 defer block을 실행하고 만약 try가 무탈하게 지나간다면 "do-statement finished"가 불리고 defer가 불릴 것이다. 그래서 정리를 해보면 아래와 같다.
68 |
69 | 1. try 실패
70 | ```
71 | 2
72 | 1
73 | catch statement
74 | ```
75 |
76 | 2. try 성공 (물론 위 코드에서는 이렇게 나올 수 없음)
77 | ```
78 | do-statement finished
79 | 2
80 | 1
81 | ```
82 |
83 | 우리는 결과를 보고 defer의 특징을 알 수 있다.
84 |
85 | 1. (당연) scope를 빠져나가기 직전에 실행 됨
86 | 2. 정의 된 역순으로 실행 됨
87 |
88 | ### Reference
89 |
90 | * [Swift Programming Language Guid](https://docs.swift.org/swift-book/ReferenceManual/Statements.html)
91 | * [How Defer Operator in Swift Actually Works](https://medium.com/@sergeysmagleev/how-defer-operator-in-swift-actually-works-30dbacb3477b)
92 | * [defer문 이해하기](https://soooprmx.com/archives/6118)
93 |
--------------------------------------------------------------------------------
/Swift/Delegate,Notification,KVO.md:
--------------------------------------------------------------------------------
1 | ## Delegate/ Notification/ KVO
2 |
3 | https://medium.com/@Alpaca_iOSStudy/delegation-notification-그리고-kvo-82de909bd29
4 |
5 | 특정 이벤트가 일어나면 원하는 객체에 알려주어 해당되는 처리를 하는 방법
6 |
7 | 객체가 다른 객체와 소통은 하지만 묶이기는 (종속되기는) 싫을 때 사용
8 |
9 | ### **Delegate**
10 |
11 | 프로토콜을 사용하여 delegate로 지정된 객체가 해야하는 메소드의 원형을 명시하고, delegate 역할의 객체는 이 프로토콜을 채택하여 구현한다.
12 |
13 | ```swift
14 | // 프로토콜 정의
15 | protocol SomeDelegate: class {
16 | func someFunction(someProperty: Int)
17 | }
18 |
19 | // delegate를 가지는 class 선언
20 | class SomeView {
21 | weak var delegate: SomeDelegate?
22 |
23 | func someTappped(num: Int) {
24 | delegate?.someFunction(someProperty: num)
25 | }
26 | }
27 |
28 | // view의 delegate로 지정 및 함수 실행
29 | class SomeController {
30 | private var view: SomeView?
31 |
32 | init() {
33 | view = SomeView()
34 | view?.delegate = self
35 | view?.someTappped(num: 1)
36 | }
37 | }
38 |
39 | // delegate 프로토콜 채택 및 구현
40 | extension SomeController: SomeDelegate {
41 | func someFunction(someProperty: Int) {
42 | print("tap \\(someProperty)")
43 | }
44 | }
45 |
46 | let controller = SomeController()
47 | ```
48 |
49 |
50 |
51 | 위의 코드를 실행하면 controller init() 함수에서 view의 someTapped() 함수를 실행하고, view의 delegate로 지정된 controller가 구현한 someFunction이 실행되게 된다
52 |
53 | 즉, view의 행동을 controller에게 위임함으로서 역할을 떠넘기기(?)함.
54 |
55 | - 특징
56 |
57 | 엄격한 문법으로 프로토콜 구현이 제대로 되어있지 않을 때 컴파일 시에 오류를 확인할 수 있다. 소통 과정을 유지하고 모니터링 할 객체가 필요 없다. 하지만 많은 줄의 코드가 필요하고, 많은 객체들에게 이벤트를 알려주는 것이 어렵고 비효율적이다.
58 |
59 | ### **Notification**
60 |
61 | Notification Center라는 싱글턴 객체로 원하는 이벤트의 observer로 등록한 객체들에게 이벤트 발생 시에 notification을 post하여 알려준다. Notification name이라는 key값 이용.
62 |
63 | - 특징
64 |
65 | 코드 구현이 쉬움. 여러 객체들에게 동시에 이벤트 발생을 알려줄 수 있다. 관련 정보를 userInfo로 전달 가능. key값으로 notification name을 이용하여 컴파일 타임에 오류를 잡아내기 어렵다.
66 |
67 | ### **KVO(Key-Value Observing)**
68 |
69 | https://www.hackingwithswift.com/example-code/language/what-is-key-value-observing
70 |
71 | 프로퍼티의 상태를 추적하여 두 객체 사이의 소통을 하는 방식. key path로 추적하여 nested objects도 추적이 가능. NSObject를 상속받는 객체에서만 사용 가능. 사용하지 않을 때에는 observer를 지워줘야 한다.
72 |
73 | willSet, didSet과의 차이는 정의부에서의 관찰이 아닌 정의부 외부에서의 관찰 행위라는 것. 또한, Objective 런타임에 의존하기 때문에 swift에서 사용하기 위해서는 `@objc` 키워드를 사용해야 한다.
74 |
75 | ```swift
76 | // @objcMembers를 사용하면 프로퍼티 앞에 @objc 생략 가능 (class에서만 사용)
77 | @objcMembers class Person: NSObject {
78 | dynamic var name = "Taylor Swift"
79 | }
80 |
81 | let taylor = Person()
82 |
83 | let observation = taylor.observe(\\.name) { person, change in
84 | print("I'm not called \\(person.name)")
85 | }
86 |
87 | taylor.name = "Justin Bieber"
88 | ```
89 |
90 | 위 코드를 실행하면 taylor의 name에 새로운 값을 할당하는 순간 observation에 저장한 closure가 실행된다.
91 |
92 | ### 언제 어떤 것을 💬
93 |
94 | (개인 의견 반영)
95 |
96 | KVO는 프로퍼티의 상태를 추적할 때 사용하지만, didSet이나 willSet으로 대체 가능하다. 프로토콜을 사용하는 delegate 패턴이 일반적으로 안전하기 때문에 권장되긴 하지만, 여러 객체에 한번에 이벤트를 전달해야할 경우에는 Notification을 사용하는게 효율적일 수도 있다.
--------------------------------------------------------------------------------
/Swift/Equatable,Comparable,Hashable.md:
--------------------------------------------------------------------------------
1 | ## Equatable, Comparable, Hashable
2 |
3 | https://jcsoohwancho.github.io/2019-10-27-Equatable,-Comparable,-Hashable/
4 |
5 | ### Equatable
6 |
7 | ==, != 연산 가능, sequence나 collection에서 contains 가능
8 |
9 | 구조체의 경우 모든 저장 프로퍼티가 equatable을 채택해야 함.
10 |
11 | 열거형의 경우, 모든 연관 값들이 equatable을 채택해야 함. 연관 값이 없을 때는 자동으로 컴파일러가 채택해 줌
12 |
13 | swift의 기본 자료형들은 대부분 equatable을 채택하고 있음
14 |
15 | 위 경우가 아닐 경우 == 연산을 직접 구현해주면 됨
16 |
17 | > Comparable과 Hashable은 Equatable 을 채택한다.
18 |
19 | ### Comparable
20 |
21 | 부등호 연산(>, <, ≥, ≤)이 가능하고, sequence나 collection에서 정렬 가능.
22 |
23 | equatable을 채택하기 때문에 == 연산과 < 연산을 구현해줘야 한다.
24 |
25 | ### Hashable
26 |
27 | Hasher를 통해 Int형의 해쉬 값을 만들어 낼 수 있도록 한다. Set과 Dictionary에서 키로 사용 가능. hash() 함수를 구현해줘야 함.
28 |
29 | hasher의 combine() 메소드로 값을 제공해 주도록 구현된다. 컴파일러가 기본 구현을 자동으로 해줄 수도 있다.
30 |
31 | ```swift
32 | func hash(into hasher: inout Hasher) {
33 | hasher.combine("변수")
34 | }
35 | ```
--------------------------------------------------------------------------------
/Swift/GCD.md:
--------------------------------------------------------------------------------
1 | ## GCD란 ?
2 |
3 | ios에서 쉽고 편한 멀티 스레딩 처리를 위해 제공되는 api
4 |
5 | 개발자가 작업 큐에 작업을 넣어주기만 하면 gcd가 알아서 스레드 관리를 해주는 느낌?
6 |
7 | 스레드를 생성하고 할당하는 관리를 시스템에게 맡겨준다.
8 |
9 | ## Dispatch Queues
10 |
11 | [iOS ) Concurrency Programming Guide - Dispatch Queues](https://zeddios.tistory.com/513)
12 |
13 | [iOS ) GCD - Dispatch Queue사용법 (2) / DispatchWorkItem, DispatchGroup](https://zeddios.tistory.com/520)
14 |
15 | 사용하기 쉽고, 해당 쓰레드 코드 보다 해당 task를 실행 할 때 훨씬 효율적이다(?)
16 |
17 | task를 비동기적으로 동시에 수행할 수 있는 손쉬운 방법이다. task는 앱이 수행해야하는 작업. dispatch queues는 제출한 (넘겨 받은?) 모든 task를 관리하는 객체와 유사한 구조이다. 모든 dispatch queues는 FIFO 구조로 항상 순서대로 시작한다.
18 |
19 |
20 |
21 | ### Serial(private dispatch queue)
22 |
23 | 큐에 추가된 순서대로 **한 번에 하나의 task**를 실행. 현재 실행 중인 task는 dispatch queues에서 관리하는 고유한 쓰레드**(task마다 다를 수 있다)**에서 실행된다.
24 |
25 | > Serial queues를 4개 작성하면 각 큐는 한 번에 하나의 task만 실행하지만, 최대 4개의 task가 각 큐에서 동시에 실행될 수 있다!
26 |
27 |
28 |
29 | ### Concurrent(global dispatch queue)
30 |
31 | **동시에 하나 이상의 task를 실행**하지만 task는 큐에 추가된 순서대로 시작. 현재 실행 중인 task는 dispatch queues에서 관리하는 고유 쓰레드에서 실행된다. 특정 시점에서 실행되는 정확한 task 수는 가변적. iOS5 이상에서는 큐 타입으로 DISPATCH_QUEUE_CONCURRENT를 지정하여 사용자가 동시에 dispatch queue를 생성할 수 있다. 앱에 사용할 사전에 정의된 global concurrent queues가 4개 존재함.
32 |
33 |
34 |
35 | ### Main Dispatch Queue
36 |
37 | 앱의 **main 쓰레드**에서 task를 실행하는, 전역적으로 사용 가능한 **serial queue**.
38 |
39 | 앱의 실행 루프와 함께 작동하여 큐에 있는 **task 실행이 실행 루프에 연결된 다른 이벤트 소스의 실행과 얽힌다**. 종종 앱의 주요 동기화 지점으로 사용된다.
40 |
41 |
42 |
43 | ### Dispatch Queue의 장점
44 |
45 | 쓰레드에 비한 장점. work-queue(작업 큐) 프로그래밍의 단순성.
46 |
47 | 쓰레드 생성 및 관리에 대한 작업을 dispatch queue에서 알아서 해준다. 시스템은 사용가능한 자원 및 현재 시스템 조건에 따라 동적으로 쓰레드 수를 조절한다. 일반적으로 쓰레드를 직접 작성한 것 보다 빨리 task를 수행한다.
48 |
49 | 쓰레드 코드 작성하는 것 보다 쉽다. 핵심은 독립적이고, 비동기적으로 실행되는 task를 설계하는 것. 쓰레드에서 공유 리소스에 접근 할 때에는 동시에 수정을 하지 못하도록 lock하는 작업을 해주어야 한다.
50 |
51 | **dispatch queues는 한 번에 하나의 task만 자원을 수정하도록 할 수 있다.** lock을 사용할 때 선점식의 경우 항상 고가의 커널 트랩을 필요로 하지만, dispatch queues는 주로 앱의 프로세스 공간에서 작동하고, 필요한 경우에만 커널을 호출한다.
52 |
53 | 쓰레드 모델은 커널과 사용자 공간 메모리를 모두 차지하는 두 개의 쓰레드를 생성해야한다. dispatch queues는 쓰레드에 대해 동일한 메모리 페널티를 지불하지 않으며, 사용하는 쓰레드는 사용 중이기 때문에 차단되지 않는다.
54 |
55 | **[특징]**
56 |
57 | - 다른 dispatch queues와 동시에 task를 실행한다. task의 직렬화는 single dispatch queue일 때만 나타난다.
58 | - 시스템은 한 번에 실행되는 총 task 수를 결정한다. 100개의 다른 큐에서 100개의 task를 가진 앱은 100개 이상의 유효코어가 없는 한 모든 task를 동시에 실행할 수 없다.
59 | - 새 task를 선택할 때 큐 priority level이 고려된다.
60 | - 큐의 task는 큐에 추가될 때, 실행할 준비가 되어 있어야 한다.
61 | - private dispatch queues는 reference-counted 객체이다. **retain count를 증가 시키기 때문에, 모든 dispatch sources가 취소 되었는지 확인하고, retain-release를 고려해야한다**.
62 |
63 |
64 |
65 | ### QoS(Quality of Service)
66 |
67 | priority가 높은 작업은 우선순위가 낮은 작업보다 더 빨리 수행되고 리소스가 많으므로 일반적으로 priority가 낮은 작업보다 더 많은 에너지가 필요하다. 적절한 QoS클래스를 지정하면, 앱이 에너지 효율적일 뿐만 아니라 반응적임을 보장할 수 있다.
68 |
69 | - **User-interactive**: ~~main thread에서 작업~~(아님). 사용자 인터페이스 새로고침 또는 애니메이션 수행처럼 사용자와 상호작용하는 작업. 작업이 신속하게 수행되지 않으면, UI가 중단된 상태로 표시될 수 있다. 반응성과 성능에 중점을 둔다.
70 | - **User-initiated**: 사용자가 시작한 작업. 저장된 문서를 열거나 사용자 인터페이스에서 무언가를 클릭할 때 작업을 수행하는 것과 같은 즉각적인 결과가 필요하다. 사용자 상호작용을 계속하려면 작업이 필요하다. 반응성과 성능에 중점. 거의 순식간이며, 몇 초 혹은 그 이하
71 | - **Utility**: 작업을 완료하는데 약간의 시간이 걸릴 수 있으며, 데이터 다운로드와 같은 즉각적인 결과가 필요하지 않음. 일반적으로 사용자가 볼 수 있는 progress bar가 있다. 반응성, 성능, 에너지 효율성 간의 균형을 유지하는데 중점. 몇 초에서 몇 분정도
72 | - **Background**: 백그라운드에서 작동. indexing, 동기화, 백업같이 사용자가 볼 수 없는 작업. 에너지 효율에 중점. 분 혹은 시간과 같은 상당한 시간이 걸림
73 |
74 | > ‼️ 사용자 작업이 발생하지 않는 시간의 90% 이상을 **Utility** QoS level에서 실행하는 것이 좋다
75 |
76 | default는 User-initiated와 Utility 사이
77 |
78 |
79 |
80 | ### 🚨QoS Promotion Rule
81 |
82 | 추후 꼭 공부하기
83 |
84 |
85 |
86 | ---
87 |
88 |
89 |
90 | ### Dispatch WorkItem
91 |
92 | 수행할 수 있는 task를 캡슐화 한 것.
93 |
94 | ```swift
95 | let wellyWorkItem = DispatchWorkItem {
96 | for i in 1...5 {
97 | print("workItem \\(i)")
98 | }
99 | }
100 |
101 | DispatchQueue.global().async(execute: wellyWorkItem)
102 | ```
103 |
104 | 다음과 같이 작업 단위를 workItem으로 나타내고 수행할 수 있다.
105 |
106 | **workItem에도 QoS를 지정할 수 있다.** 따라서 같은 queue 내에서의 작업의 우선순위를 지정해 줄 수 있다. **그치만 보장은 안됨!!** 그냥 대략 그렇다는 것 ,,
107 |
108 | ### Dispatch Groups
109 |
110 | 완료(completion)를 위해 블록 객체 집합을 모니터링 하는 방법. 동기적/비동기적으로 블록을 모니터링 할 수 있다. 다른 task의 완료 여부에 따라 코드에 유용한 동기화 매커니즘을 제공한다.
111 |
112 | ```swift
113 | let myGroup = DispatchGroup()
114 |
115 | myQueue.async(group: myGroup) {
116 | for i in 100...105 {
117 | print("🏓 \\(i)")
118 | }
119 | }
120 |
121 | myQueue.async(group: myGroup) {
122 | for i in 1...5 {
123 | print("🚩 \\(i)")
124 | }
125 | }
126 |
127 | myGroup.notify(queue: myQueue) {
128 | print("End!")
129 | }
130 | ```
131 |
132 | 다음과 같이 해당 그룹의 해당 큐의 모든 작업이 완료되었을 때 End를 출력해준다.
133 |
134 | ```swift
135 | myGroup.notify(queue: myQueue) {
136 | print("End!")
137 | myQueue.async(group: myGroup) {
138 | for i in 200...205 {
139 | print("hi \\(i)")
140 | }
141 | }
142 |
143 | myQueue.async(group: myGroup) {
144 | for i in 300...305 {
145 | print("bye \\(i)")
146 | }
147 | }
148 |
149 | myGroup.notify(queue: myQueue) {
150 | print("Real End!!!!!")
151 | }
152 |
153 | }
154 | ```
155 |
156 |
157 |
158 | notify 안에 notify를 호출할 수도 있음
159 |
160 | 비동기 작업에서의 순서를 보장하고 싶을 때 유용하게 사용가능
161 |
162 |
163 |
164 | ## 그 외의 기능들
165 |
166 | ### Dispatch Semaphores(?)
167 |
168 | 세마포어를 사용할 수 없어서 호출 쓰레드를 차단해야하는 경우에만 세마포어가 커널로 호출된다. 세마포어를 사용할 수 있으면, 커널 호출이 수행되지 않는다
169 |
170 |
171 |
172 | ### Dispatch Sources
173 |
174 | 특정 타입 시스템 이벤트에 대한 응답으로 notifications을 생성한다. dispatch sources를 사용하여 프로세스 notifications, signal, descriptor events 등을 모니터링 할 수 있다. 이벤트가 발생하면 dispatch sources는 처리를 위해 지정된 dispatch queue에 비동기적으로 task 코드를 제출한다.
175 |
176 |
177 |
178 |
179 |
180 | ---
181 |
182 |
183 |
184 | ### +) 왜 main.sync를 하면 안될까?
185 |
186 | [iOS ) 왜 main.sync를 하면 안될까](https://zeddios.tistory.com/519)
187 |
188 | UI 업데이트는 반드시 main에서 해야한다
189 |
190 | → UIApplication 인스턴스가 main thread에 붙고, UIApplication은 앱의 run loop를 포함해서 main event loop를 설정한다. main event loop는 모든 UI event를 수신.
191 |
192 | UI event는 **UIApplication → UIWindow → UIViewController → UIView → subViews..** 와 같이 responder chain을 따라 UIResponder로 전달된다.
193 |
194 | 따라서 모든 이벤트는 main thread의 일부가 된다.
195 |
196 | 만약 background thread에서 view를 지우고 그 thread의 run loop가 끝나지 않았을 때 사용자가 그 view를 탭하게 되면 touch event와 충돌하게 될 것이다.
197 |
198 | sync는 큐에 있는 작업이 끝날 때까지 block하고 wait 상태가 된다.
199 |
200 | 만약 main queue에서 sync를 호출하여 큐를 block하게 되면 deadlock 현상이 발생한다.
201 |
202 | **main thread는 thread-safe하지 않다!**
--------------------------------------------------------------------------------
/Swift/HigherOrderFunction.md:
--------------------------------------------------------------------------------
1 |
2 | ## 고차함수와 관련 된 면접 질문
3 |
4 | * 함수형 프로그래밍이 무엇인지 설명하시오. [출처](https://github.com/JeaSungLEE/iOSInterviewquestions#functional-programming)
5 | * 고차 함수가 무엇인지 설명하시오. [출처](https://github.com/JeaSungLEE/iOSInterviewquestions#functional-programming)
6 | * Swift Standard Library의 map, filter, reduce, compactMap, flatMap에 대하여 설명하시오. [출처](https://github.com/JeaSungLEE/iOSInterviewquestions#functional-programming)
7 |
8 | # 고차함수
9 |
10 | ## Functional Programming(함수형 프로그래밍)이란
11 |
12 | ## Higher Order Function
13 |
14 | ### map
15 |
16 | ### filter
17 |
18 | ### reduce
19 |
20 | ### compactMap
21 |
22 | ### flatMap
23 |
--------------------------------------------------------------------------------
/Swift/Initialization.md:
--------------------------------------------------------------------------------
1 | ## Two-Phase Initialization
2 |
3 | https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID220
4 |
5 | Swift에서 클래스는 2단계로 초기화 된다. 첫 번째는 각 저장 프로퍼티들이 **그 클래스에 의해 초기 값을 할당** 받는다. 모든 저장 프로퍼티의 초기 값이 결정되면, 두 번째 단계가 시작된다. 각 클래스는 새로운 인스턴스의 사용 준비가 끝나기 전에 **각 저장 프로퍼티를 커스터마이즈 할 기회를 가진다.**
6 |
7 | 2단계의 초기화 과정은 클래스 계층에서의 각 클래스의 완전한 유연함을 제공하면서 안전하게 초기화 할 수 있도록 해준다. 또한 프로퍼티 값이 초기화되기 전에 접근하는 것을 막아주고, 다른 예기치 않은 이니셜라이저에 의해 다른 값으로 설정되는 것을 방지한다.
8 |
9 | Swift의 two-phase initialization은 Objective-C에서의 초기화와 비슷하다. 가장 큰 차이점은 첫 번째 단계에서 Objective-C는 모든 프로퍼티에 0이나 nil 값을 할당하지만, Swift는 사용자 지정 초기 값을 지정할 수 있어 유연하고 0이나 nil이 기본 값으로 적합하지 않는 타입들을 다룰 수 있다.
10 |
11 | Swift 컴파일러는 two-phase initialization이 에러없이 완료되는지 확인하기 위해 4개의 safety-checks를 수행한다.
12 |
13 | ### Safety check1
14 |
15 | *지정된 이니셜라이저는 모든 프로퍼티가 superclass 이니셜라이저까지 위임되기 전에 초기화 되어야 한다.*
16 |
17 | 객체의 메모리는 모든 프로퍼티의 초기 상태가 있어야만 완전히 초기화 된 걸로 간주된다. 이 조건을 충족시키기 위해서는 현재 이니셜라이저가 체인을 넘겨주기 전에 (superClass의 initializer를 호출하기 전에) 모든 프로퍼티의 초기화를 진행해야 한다.
18 |
19 | ```swift
20 | class SuperClass {
21 | var name: String
22 | var age: Int
23 |
24 | init(name: String, age: Int) {
25 | self.name = name
26 | self.age = age
27 | }
28 | }
29 |
30 | class SubClass: SuperClass {
31 | var type: String
32 |
33 | init(type: String) {
34 | super.init(name: "hi", age: 13)
35 | self.type = type // error
36 | }
37 | }
38 | ```
39 |
40 | 다음과 같이 SubClass의 self.type이 초기화 되지 않은 상태에서 super class이 생성자를 호출할 수 없다.
41 |
42 | ### Safety check2
43 |
44 | *지정된 이니셜라이저는 상속된 프로퍼티에 값을 할당하기 전에 super class의 이니셜라이즈를 호출해야한다. 그렇지 않으면 지정된 이니셜라이저가 할당한 새 값은 super class의 이니셜 라이저에서 다시 쓰여질 것이다.(overwritten)*
45 |
46 | ```swift
47 | class SubClass: SuperClass {
48 | var type: String
49 |
50 | init(type: String) {
51 | self.type = type
52 | self.age = 11 // error
53 | super.init(name: "hi", age: 13)
54 | }
55 | }
56 | ```
57 |
58 | ### Safety check3
59 |
60 | *convenience 이니셜라이저는 어떤 프로퍼티(같은 클래스에 의해 정의된 프로퍼티 포함)에 값을 할당하기 전에 다른 이니셜라이저를 호출해야한다. 그렇지 않으면 convenience 이니셜라이저가 할당한 새 값은 지정된 이니셜라이저에 의해 다시 쓰여질 것이다.*
61 |
62 | ```swift
63 | convenience init(age: Int) {
64 | self.name = "welly" // error
65 | self.init(name: "welly", age: age)
66 | }
67 | ```
68 |
69 | ### Safety check4
70 |
71 | *이니셜라이저는 첫 번째 초기화가 완료되기 전까지 어떠한 인스턴스 메서드 호출, 인스턴스 프로퍼티 값 읽기, self 참조 등을 할 수 없다.*
72 |
73 | 클래스 인스턴스는 첫 번째 초기화가 끝나기 전까지는 유효하지 않은 상태이다. 첫 번째 초기화가 끝나고 유효한 상태가 되어야지만 프로퍼티에 접근하거나 메소드를 호출할 수 있다.
74 |
75 |
76 |
77 | ### 위 4 가지 규칙들에 의한 two-phase 초기화 방법
78 |
79 | **[Phase 1]**
80 |
81 | - 지정 이니셜라이저 (그냥 init)이나 convenience 이니셜라이저가 class 에서 호출
82 | - 해당 클래스의 새로운 인스턴스를 위한 메모리 할당. 메모리는 아직 초기화 되지 않은 상태
83 | - 지정 이니셜라이저는 모든 저장 프로퍼티가 해당 클래스에 의해 초기 값을 가지고 있는지 확인. 저장 프로퍼티에 대한 메모리가 현재 초기화 됨
84 | - 지정 이니셜라이저는 super class의 이니셜라이저를 호출하여 저장 프로퍼티에 대해 같은 작업을 수행
85 | - 체인의 맨 위에 도달할 때까지 클래스 상속 체인을 거슬러 올라감
86 | - 가장 상위에 도달하여 마지막 class가 모든 저장 프로퍼티가 값을 가짐을 보장하면, 인스턴스의 메모리가 완전히 초기화되었다고 간주하고 phase 1 초기화 종료
87 |
88 | **[Phase 2]**
89 |
90 | - 체인의 상단에서부터 다시 내려오면서 각 지정 이니셜라이저는 인스턴스를 추가로 커스터마이징 할 수 있음. 이니셜라이저는 self에 접근 가능하고 프로퍼티 수정, 인스턴스 메소드 호출 등의 작업이 가능
91 | - 마지막으로, 체인의 어떠한 convenience 이니셜라이저도 인스턴스를 커스터마이징 가능하고 self를 호출 가능
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | 예를 들어, 위의 서브클래스의 convenience 이니셜라이저 호출로 시작되었다고 보자. convenience 이니셜라이저는 현재 어떤 프로퍼티도 변경할 수 없다. 같은 클래스의 지정 이니셜라이저로 간다.
100 |
101 | 지정 이니셜라이저는 현재 서브클래스의 모든 프로퍼티 값이 초기화 되었는지 확인한다. **(safety check1)** 그 후 슈퍼클래스의 지정 이니셜라이저로 체인을 타고 올라간다.
102 |
103 | 슈퍼클래스의 지정 이니셜라이저도 모든 슈퍼클래스의 프로퍼티 값이 초기화되었는지 확인하고 더이상 올라갈 체인이 없으면 (슈퍼클래스의 슈퍼클래스가 없으면) 모든 프로퍼티 값이 초기화되고, 메모리도 완전히 초기화 되었다. - Phase1 끝
104 |
105 |
106 |
107 |
108 |
109 | 이제 슈퍼클래스의 지정 이니셜라이저는 인스턴스를 추가로 커스터마이징 할 수 있다. 슈퍼 클래스의 커스터마이징이 끝나면, 서브 클래스의 지정 이니셜라이저가 커스터마이징 할 수 있다. (안해도 됨) 마지막으로 서브클래스의 지정 이니셜라이저의 커스터마이징이 끝나면 원래 호출되었던 convenience 이니셜라이저가 추가적인 커스터마이징을 할 수 있게 된다. - Phase2 끝
110 |
111 |
112 |
113 | ## Failable Initializers
114 |
115 | https://wlaxhrl.tistory.com/49
116 |
117 | 초기화를 하다가 실패할 경우가 있는 경우는 `init?()` 과 같은 형식으로 failable init을 할 수 있다.
118 |
119 | failable init과 designed init을 동일한 파라미터 타입/이름으로 둘 다 정의는 안된다.
120 |
121 | failable initializer는 초기화하는 타입의 **옵셔널 값을 생성**한다.
122 |
123 | ```swift
124 | init?(species: String) {
125 | if species.isEmpty { return nil }
126 | self.species = species
127 | }
128 | ```
129 |
130 | 다음과 같이 실패할 경우에는 nil을 반환하도록 한다.
131 |
132 | ```swift
133 | enum TemperatureUnit {
134 | case kelvin, celsius, fahrenheit
135 | init?(symbol: Character) {
136 | switch symbol {
137 | case "K":
138 | self = .kelvin
139 | case "C":
140 | self = .celsius
141 | case "F":
142 | self = .fahrenheit
143 | default:
144 | return nil
145 | }
146 | }
147 | }
148 | ```
149 |
150 | enum에서는 해당하는 경우가 없을 경우 초기화에 실패하고 싶을 때 사용
151 |
152 |
153 |
154 | ## Requierd Initializers
155 |
156 | 클래스의 이니셜라이저에 required 수식어를 붙이면 해당 클래스를 상속받는 모든 서브클래스는 해당 이니셜라이저를 구현해야 한다. override 키워드는 안붙여도 된다.
--------------------------------------------------------------------------------
/Swift/MemoryReference.md:
--------------------------------------------------------------------------------
1 | # Q. ARC가 뭔가요?
2 |
3 | ARC는
4 |
5 | - **컴파일 시 코드를 분석해서 자동으로 retain, release 코드를 생성해주는 것**
6 | - **참조된 횟수를 추적해 더 이상 참조되지 않는 인스턴스를 메모리에서 해제해주는 것**
7 |
8 | 입니다.
9 |
10 |
11 |
12 | ### 메모리 영역
13 |
14 | 1. **코드 영역**
15 |
16 | 작성한 소스코드가 기계어 형태로 저장
17 |
18 | 컴파일 타임에 결정되고 , 중간에 코드가 변경되지 않도록 read-only 형태로 저장
19 |
20 | 2. **데이터 영역**
21 |
22 | 전역변수, static 변수 저장
23 |
24 | 프로그램 시작과 동시에 할당되고, 프로그램이 종료 되어야 메모리가 해제
25 |
26 | 실행 도중 변수 값이 변경될 수 있으니 read-write로 저장
27 |
28 | 3. **힙 영역**
29 |
30 | 프로그래머가 할당/해제 하는 영역 (동적 할당)
31 |
32 | 사용하고 난 후에는 반드시 메모리 해제를 해줘야한다. 그렇지 않으면 memory leak이 발생
33 |
34 | → swift는 `ARC`가 자동으로 해제해줌
35 |
36 | 런타임 시 결정되기 때문에 데이터 크기가 확실하지 않을 때 사용
37 |
38 | class, 클로저 같은 참조 타입 + String 할당
39 |
40 | 4. **스택 영역**
41 |
42 | 함수 호출 시 함수의 지역변수, 매개변수, 리턴 값 저장 함수가 종료되면 메모리도 해제
43 |
44 | 컴파일 타임에 결정되기 때문에 무한히 할당할 수 없다
45 |
46 | struct 내에 둘 이상의 참조타입이 존재하면 그 레퍼런스 카운팅을 유지하는 비용이 class로 만들 때보다 증가할 수 있다.
47 |
48 | ### 앱 메모리
49 |
50 |
51 |
52 | Clean 메모리는 이미지, 프레임워크 데이터 등을 포함하며, Dirty 메모리는 객체 등 앱에서 수정한 데이터들과, 프레임워크 dirty 메모리 등을 포함합니다.
53 |
54 | Compressed memory는 문자 그대로 압축된 메모리를 의미하는데, 일정 기간 동안 특정 메모리 영역에 접근하지 않으면, 시스템이 해당 메모리 페이지들을 압축하고, 다시 접근할 때 압축 해제합니다. 이런 압축된 영역을 Compressed memory라고 합니다. iOS는 Memory compressor를 이용하여 이런 압축 또는 압축 해제 작업들을 수행합니다.
55 |
56 | 또한 앱이 할당 받을 수 있는 메모리 Footprint에는 제한 한도가 존재하며, 이 한도치를 넘어가면 `EXC_RESOURCE_EXCEPTION` 익셉션이 발생합니다.
57 |
58 | footprint? 특정 하드웨어나 소프트웨어 단위가 차지하고 있는 공간의 크기
59 |
60 |
61 |
62 | ## 메모리 릭
63 |
64 | 앱의 메모리 릭을 캐치하는 좋은 접근 방법은, 앱의 특정 flow를 여러번 반복하면서 반복할 때마다 메모리 그래프 디버거를 이용해 메모리 스냅샷을 찍어 비교하는 것 입니다.
65 |
66 | retain cycle? 메모리가 해제되지 않고 유지되어 누수가 발생하는 현상
67 |
68 | > delegate는 weak으로 선언하여 메모리 누수를 예방하여야 한다
69 |
70 | → 이럴 경우 delegate와 관련된 프로토콜은 class 전용이어야 한다!!! 왜? unowned나 weak는 reference count 관리(즉 class)를 위한 개념이기 때문에!
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | # Weak And Unowned References
79 |
80 | https://krakendev.io/blog/weak-and-unowned-references-in-swift
81 |
82 | ## Strong
83 |
84 | 일반적인 참조(포인터와 같은 것들)이지만 ARC에 의해 해제되지 않도록 reference count을 1 증가시킨다는 점에서 특별하다. 본질적으로, 어떠한 것이 객체를 strong 참조하는 동안 객체는 절대 해제되지 않을 것이다.
85 |
86 | property 선언에서 default로 strong 참조를 가진다. 일반적으로 객체의 계층이 **선형적**으로 이루어져 있을 때 안전하게 사용할 수 있다. 부모 → 자식으로 강한 참조 관계일 때 항상 ok이다.
87 |
88 | ## Weak
89 |
90 | weak 참조는 ARC로 부터 객체가 해제되는 것을 막지 않는다. 또한 객체가 할당 해제되면 포인터는 nil이 된다. 이렇게 하면 weak 참조는 항상 객체에 접근할 때 유효한 객체이거나 nil이거의 상태가 된다.
91 |
92 | 모든 weak 참조 변수는 let이 아닌 var여야 한다.
93 |
94 | → 참조되고 있지 않은 객체는 nil로 변경되기 때문 (mutable)
95 |
96 | weak 참조는 retain cycle이 일어날 가능성이 있을 때 사용하는 것이 중요하다. 두 객체가 서로를 strong하게 참조하고 있으면, ARC는 두 객체에 적절한 release message를 전달할 수 없다.
97 |
98 | 예를 들어, 클로져 범위 밖에 변수가 선언되어 있고, 클로져 범위 내에서 그 변수를 참조하면 또 다른 strong 참조가 생성되게 된다. 단, 값의 의미(value semantics)를 가지는 것들은 예외.
99 |
100 | ```swift
101 | class Kraken {
102 | var notificationObserver: ((Notification) -> Void)?
103 | init() {
104 | notificationObserver = NotificationCenter.default.addObserver(forName: "humanEnteredKrakensLair", object: nil, queue: .main) { notification in
105 | self.eatHuman()
106 | }
107 | }
108 |
109 | deinit {
110 | NotificationCenter.default.removeObserver(notificationObserver)
111 | }
112 | }
113 | ```
114 |
115 | 다음과 같은 경우에서 NotificationCenter는 eatHuman()을 호출할 때 self를 강하게 참조하는 클로져를 유지한다. deinit을 할 때 observer를 지우는 것이 적절하겠지만, 해당 observer는 kraken에 의해 strong 참조 관계를 가지기 때문에 ARC에서 deinit을 하지 않는다. `NSTimers` 와 `NSThread` 에서도 이러한 일이 일어난다.
116 |
117 | 해결방법은 클로저의 캡쳐 리스트에서 self를 weak 참조하는 것이다. 이는 강한 순환 참조를 끊는다. weak 참조일 때는 ARC에 의해 reference count가 증가되지 않는다.
118 |
119 | ```swift
120 | //Look at that sweet, sweet Array of capture values.
121 | let closure = { [weak self, unowned krakenInstance] in
122 | self?.doSomething() //weak variables are Optionals!
123 | krakenInstance.eatMoreHumans() //unowned variables are not.
124 | }
125 | ```
126 |
127 | 캡쳐 리스트에서 여러 캡쳐 값을 지정할 수 있다.
128 |
129 | 또 한가지 경우는 클래스에서 delegation을 지정하기 위해 프로토콜을 사용할 경우이다. struct나 enum도 프로토콜을 채택할 수 있지만 클래스와 달리 value semantics이다.
130 |
131 | delegate property를 weak로 선언하여 retain cycle을 방지할 수 있지만, 이 경우의 protocol은 class를 상속받아 reference semantics임을 밝혀야 한다.
132 |
133 | ### : class
134 |
135 | 해당 프로토콜의 요구조건에 의해 정의된 동작이 값 의미론보다는 참조 의미론을 가지고 있다고 가정하거나 요구될 때 클래스 전용 프로토콜을 사용한다.
136 |
137 | ### value semantics?
138 |
139 | The only exceptions to this are variables that use value semantics such as Ints, Strings, Arrays, and Dictionaries in Swift.
140 |
141 | https://oaksong.github.io/2017/12/29/value-semantics-vs-reference-semantics/
142 |
143 | - 추후 참고할 자료
144 |
145 | [Value SEMANTICS (not value types!)](https://academy.realm.io/posts/swift-gallagher-value-semantics/)
146 |
147 | ## Unowned
148 |
149 | unowned는 weak와 유사하게 reference count를 증가시키지는 않지만, optional 값이 아니라는 이점을 가지고 있다. 이는 optional binding의 수고를 덜어준다.
150 |
151 | 또한 객체가 해제되고 나면, dagling pointer가 된다.
152 |
153 | weak reference는 객체의 lifetime에 nil이 될 수 있는 경우, 반대로 참조가 nil이 되지 않을 때 즉, 참조하고 있는 객체와 같거나 더 긴 lifetime을 가지고 있을 때는 unowned reference를 쓰라고 apple 문서에 적혀있다.
154 |
155 | - unowned를 사용해도 되는 경우의 예시
156 |
157 | ```swift
158 | class RetainCycle {
159 | var closure: (() -> Void)!
160 | var string = "Hello"
161 |
162 | init() {
163 | closure = { [unowned self]
164 | self.string = "Hello, World!"
165 | }
166 | }
167 | }
168 |
169 | //Initialize the class and activate the retain cycle.
170 | let retainCycleInstance = RetainCycle()
171 | retainCycleInstance.closure()
172 | //At this point we can guarantee the captured self inside the closure will not be nil. Any further code after this (especially code that alters self's reference) needs to be judged on whether or not unowned still works here.
173 | ```
174 |
175 | 클로저가 서로 상호의존적일 때 ( 하나가 다른 하나 없이는 live할 수 없을 때), weak 보다는 unowned를 쓰는 것이 프로그램이 불필요하게 nil 참조를 유지하는 overhead를 막아준다.
176 |
177 | ```swift
178 | class Kraken {
179 | let petName = "Krakey-poo"
180 | lazy var businessCardName: (Void) -> String = { [unowned self] in
181 | return "Mr. Kraken AKA " + self.petName
182 | }
183 | }
184 | ```
185 |
186 | 위의 경우에서 `businessCardName` 클로저와 클로저 내부의 self는 상호 의존적이기 때문에 unowned reference를 사용한다.
187 |
188 | ```swift
189 | class Kraken {
190 | let petName = "Krakey-poo"
191 | lazy var businessCardName: String = {
192 | return "Mr. Kraken AKA " + self.petName
193 | }()
194 | }
195 | ```
196 |
197 | 그러나 다음과 같이 클로저가 아닌 lazy 변수를 사용할 때와 혼동을 피해야한다.
198 |
199 | lazy 변수를 호출하는 클로저를 실제로 유지하는 것은 없기 때문에 unowned self는 필요하지 않다. 변수는 오직 클로저의 결과 값 string만 자신에게 할당하고, 클로저는 사용 후 바로 해제되기 때문.
200 |
201 | ## Unowned Optional References
202 |
203 | https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID57
204 |
205 | unowned 객체를 optional로 지정할 수 있다. weak reference와 동일한 문맥에서 사용가능하다. 차이는 개발자가 유효한 객체를 참조하는지 보장하거나 nil 값으로 설정해 주어야한다는 점이다.
206 |
207 | ```swift
208 | class Department {
209 | var name: String
210 | var courses: [Course]
211 | init(name: String) {
212 | self.name = name
213 | self.courses = []
214 | }
215 | }
216 |
217 | class Course {
218 | var name: String
219 | unowned var department: Department
220 | unowned var nextCourse: Course?
221 | init(name: String, in department: Department) {
222 | self.name = name
223 | self.department = department
224 | self.nextCourse = nil
225 | }
226 | }
227 | ```
228 |
229 | 다음 상황에서 course는 두 개의 unowned 변수를 가진다. 각 course는 department에 속하므로 department는 optional 값이 아니지만 nextCourse는 있을 수도 없을 수도 있기 때문에 optional 값이다.
230 |
231 | ```swift
232 | let department = Department(name: "Horticulture")
233 |
234 | let intro = Course(name: "Survey of Plants", in: department)
235 | let intermediate = Course(name: "Growing Common Herbs", in: department)
236 | let advanced = Course(name: "Caring for Tropical Plants", in: department)
237 |
238 | intro.nextCourse = intermediate
239 | intermediate.nextCourse = advanced
240 | department.courses = [intro, intermediate, advanced]
241 | ```
242 |
243 |
244 |
245 | intro, intermediate 코스는 다음 코스를 가지고 있다. unowned optional reference는 ARC에서 reference count를 증가시키지 않기 때문에 nil이 될 수 있다는 점을 제외하고는 unowend reference와 같은 방식으로 동작한다.
246 |
247 | unowend reference와 같이 nextCourse는 항상 해제되지 않은 객체를 참조하고 있어야 하지 때문에 course를 삭제할 경우에는 그 course를 nextCourse로 참조하는 관계 역시 삭제해주어야 한다.
248 |
249 | optional은 enum이며, 값 타입은 unowned로 취급될 수 없다의 예외적 상황이다.
250 |
251 | optional로 포장된 class는 reference counting을 사용하지 않기 때문에 strong reference로 유지할 필요가 없다.
252 |
253 | ## Unowned References and Implicitly Unwrapped Optional Properties
254 |
255 | 어느 객체의 변수도 nil이 될 수 없고, 값을 가져야 할 때가 있다.
256 |
257 | ```swift
258 | class Country {
259 | let name: String
260 | var capitalCity: City!
261 | init(name: String, capitalName: String) {
262 | self.name = name
263 | self.capitalCity = City(name: capitalName, country: self)
264 | }
265 | }
266 |
267 | class City {
268 | let name: String
269 | unowned let country: Country
270 | init(name: String, country: Country) {
271 | self.name = name
272 | self.country = country
273 | }
274 | }
275 | ```
276 |
277 | 다음 상황에서 contry는 항상 capitalCity를 가지고 City도 Country 변수를 참조한다. City는 Country가 initialize될 때 생성되는데, Country가 완전히 초기화 되기 전까지 City의 생성자에 self를 넘길 수 없다.
278 |
279 | 이에 대처하기 위해 Country의 capitalCity의 변수를 암시적 추출 옵셔널로 선언한다. 이것은 capitalCity의 default는 nil이지만, 암묵적으로 이 값을 unwrap할 필요없이 접근할 수 있다는 것을 의미한다. 따라서 Country 객체는 name 변수가 설정되자마자 완전히 초기화 된 것으로 간주된다. 따라서 Country는 capitalCity의 init에 자기 자신 self를 넘겨줄 수 있다.
280 |
281 | 이를 [Two-Phase Initialization](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID220)이라고 함.
282 |
283 |
284 |
285 | ```swift
286 | var country = Country(name: "Canada", capitalName: "Ottawa")
287 | print("\\(country.name)'s capital city is called \\(country.capitalCity.name)")
288 | // Prints "Canada's capital city is called Ottawa"
289 | ```
290 |
291 | 이것은 strong reference 없이도 단일 구문으로 Country, City 인스턴스를 생성할 수 있으며 optional 값을 느낌표로 unwrap 할 필요없이 capitalCity에 바로 접근 가능하게 한다.
292 |
293 | 따라서 capitalCity는 초기화 된 후 non-optional 타입처럼 사용가능하고, 강한 순환 참조 역시 피할 수 있다!
294 |
295 | ## Discover Side Table
296 |
297 | https://maximeremenko.com/swift-arc-weak-references
298 |
299 | Side Table은 추가적인 객체의 정보를 저장하는 별도의 메모리 공간이다. 객체는 초기에 side Table을 가지고 있지 않는다. 객체가 weak 참조 될 때, strong 이나 unowned 카운터가 overflow 될 때 (32-bit 시스템에서 inline count는 작다) side Table이 생성된다.
300 |
301 |
302 |
303 | side Table과 객체는 서로 가리키는 포인터가 있다. 주목할 점은 weak 참조는 side Table을 가리키고 strong과 unowned는 객체를 직접 가리킨다는 것이다. 이것은 객체를 완전히 해제되게 만든다. 따라서 언제 쓰일지 모르는 weak 참조를 위해 메모리에서 좀비 상태로 존재하는 공간을 비워낼 수 있다.
304 |
305 |
306 |
307 | ### Swift Object Life Cycle
308 |
309 | https://devsday.ru/blog/details/1781
310 |
311 |
--------------------------------------------------------------------------------
/Swift/NestedFunctions.md:
--------------------------------------------------------------------------------
1 | ## Nested Functions
2 |
3 | https://docs.swift.org/swift-book/LanguageGuide/Functions.html
4 |
5 | 함수 내부 안에 함수를 정의할 수 있는데, 이를 `nested functions` 라고 한다. (이하 중첩 함수)
6 |
7 | 중첩 함수는 기본적으로 바깥 세계에서 숨겨져 있지만, 그들의 폐쇄 함수(감싸고 있는 함수를 말하는 듯)에 의해 호출되고, 사용될 수 있다. 폐쇄 함수는 그 들의 중첩 함수 중 하나를 반환하여 다른 범위에서 사용가능하게 할 수도 있다.
8 |
9 | ```swift
10 | // 내부에 함수 정의를 가진 중첩 함수
11 | func chooseStepFunction(backward: Bool) -> (Int) -> Int {
12 | func stepForward(input: Int) -> Int { return input + 1 }
13 | func stepBackward(input: Int) -> Int { return input - 1 }
14 | return backward ? stepBackward : stepForward
15 | }
16 |
17 | var currentValue = -4
18 | // 함수를 변수에 저장!
19 | let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
20 |
21 | while currentValue != 0 {
22 | print("\\(currentValue)")
23 | currentValue = moveNearerToZero(currentValue)
24 | }
25 |
26 | print("zero!")
27 | ```
28 |
29 | chooseStepFunction() 함수 내부에 새로운 함수 정의가 되어있고, 중첩 함수를 반환시켜 함수 외부에 저장해서 사용하고 있다.
30 |
31 |
--------------------------------------------------------------------------------
/Swift/Optional.md:
--------------------------------------------------------------------------------
1 | ### Optional과 관련된 면접 질문
2 |
3 | * Optional이 무엇인지 설명하시오. [출처](https://github.com/JeaSungLEE/iOSInterviewquestions)
4 | * Optional이 Swift에서 어떻게 구현되어 있는지 설명하시오. [출처](https://soojin.ro/blog/interview)
5 | * Optional의 값 추출 방식에 대해 설명하시오.
6 | * guard 구문을 활용한 optional binding에 대해 설명하시오.
7 | * Optional Chaining이 무엇인지 설명하시오.
8 |
9 |
10 | # Optional
11 |
12 | ## Optional의 기본
13 |
14 | ```swift
15 | public enum Optional: ExpressibleByNilLiteral {
16 | case none // abscence of value
17 | case some(Wrapped) // presence of a value, stored as `Wrapped`
18 | }
19 | ```
20 |
21 | Optional은 **변수나 상수 등에 값이 있음이 보장되지 않을 때 사용하는 기능**으로 Swift 언어의 특징 중 하나인 안전성(safe)이 보장되는 방법 중 하나다.
22 | 열거형으로 정의되어 있는 optional은 none과 some 케이스를 갖는데 이 때 none은 값이 없음을, some은 값이 있음을 나타낸다.
23 | 하지만 유효한 값이 존재하더라도 some의 연관값 Wrapped에 할당되어있기 때문에 이 값을 사용하기 위해서는 **3가지 방법** 중 하나를 이용하여 값을 추출해내야 한다.
24 |
25 |
26 | > < **Optional... 조금 더** 😀 >
27 | > Optional을 사용하며 반환할 수 있는 nil은 Objective-C에서 반환되던 nil과는 다르다.
28 | > Objective-C의 경우 유효한 **객체** 가 없을 때 함수에서 nil 포인터를 반환할 수 있다. 참조형식에 대해서만 적용이 가능한 개념이다.
29 | > 반면 Swift의 optional은 모든 타입에 적용이 가능해 Objective-C보다 표현성이 더 뛰어나다.
30 |
31 | ## Optional의 값 추출 방식
32 |
33 | Optional을 추출하는 방법은 3가지가 있다.
34 |
35 | * 강제 추출 (Forced Unwrapping / Unconditional Unwrapping)
36 | * 옵셔널 바인딩 (Optional Binding)
37 | * 암시적 추출 옵셔널 (Implicitly Unwrapped Optional)
38 |
39 | ### 강제 추출
40 |
41 | Optional로 선언한 상수/변수에 값이 있음이 **확실**할 때 !를 통해 강제 추출을 할 수 있다. 그러나 강제 추출을 시도했는데 반환 값이 nil이라면 **런타임 오류**가 발생한다.
42 |
43 | ```swift
44 | let a: Int? // 자동으로 nil 할당
45 | print(a!) // Fatal error: Unexpectedly found nil while unwrapping an Optional value
46 | ```
47 |
48 | ### 옵셔널 바인딩
49 |
50 | Optional binding은 optional에 값이 있는지 확인하기 위해 사용하며 if 또는 while 구문 등과 결합하여 사용한다.
51 | 값이 있다면 추출한 값을 scope 내에서만 사용 가능한 상수/변수로 할당하여 사용할 수 있게끔 만들 수 있다. 만약 값이 없다면 블록 내부 명령문은 실행되지 않는다.
52 |
53 | ```swift
54 | var address: String? = "Seoul, Korea"
55 | if let addr = address {
56 | print(addr)
57 | }
58 | ```
59 | Optional binding은 guard 문을 이용할 수도 있다. if 문은 if scope 내부에서 쓰기 위한 변/상수에 값을 넣었다면 guard 문에서는 optional binding된 상수를 guard 구문 실행 후 지역 상수처럼 쓸 수 있다. 하지만 guard 문은 언제나 else와 함께 쓰여 guard 문보다 상위 코드 블록을 종료하는 코드를 작성해야 하므로 예외사항을 처리해야 할 때 사용하면 좋다.
60 |
61 | ```swift
62 | // URLSession dataTask를 통해 data, response, error 받아서 data 처리
63 | guard let data = data else {
64 | completion(.failure(.APIInvalidResponse))
65 | return
66 | }
67 | ```
68 |
69 | ### 암시적 추출 옵셔널
70 |
71 | 프로젝트 구조/프로그램의 로직 상 optional이 무조건 값이 있다면 optional binding을 통해 추출하는 방식이 번거로울 것이다.
72 | 이 때 optional로 만들고자 하는 변수나 상수의 타입 뒤에 ? 대신 !를 붙여 implicitly unwrapped optionals를 사용하면 된다.
73 | 암시적 추출 옵셔널을 주로 볼 수 있는 경우는 바로 참조 순환을 피하기 위한 class를 초기화할 때 이다.([바로가기](https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html#ID55))
74 |
75 | ```swift
76 | class Country {
77 | let name: String
78 | var capitalCity: City!
79 | init(name: String, capitalName: String) {
80 | self.name = name
81 | self.capitalCity = City(name: capitalName, country: self)
82 | }
83 | }
84 |
85 | class City {
86 | let name: String
87 | unowned let country: Country
88 | init(name: String, country: Country) {
89 | self.name = name
90 | self.country = country
91 | }
92 | }
93 | ```
94 |
95 | ## Optional Chaining
96 |
97 | Optional chaining은 현재 nil일 수도 있는 properties, methods 와 subscripts를 호출하거나 값을 가져올 때 사용하는 방법으로 optional을 반복적으로 사용하여 중첩시킨다. Optional chaining을 통한 접근 역시 optional을 반환하므로 추출 방식 중 하나를 선택해서 진행해야한다.
98 |
99 | ```swift
100 | let person: Person = Person(name: "Sueaty")
101 | if let roomNumber: Int = person.address?.room?.number {
102 | print("Sueaty stays in room: #\(roomNumber)")
103 | } else {
104 | print("Cannot find room number")
105 | }
106 | ```
107 |
108 | #### Reference
109 |
110 | * [Optional](https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html)
111 | * [Swift Language Guid - Optional Chaining](https://docs.swift.org/swift-book/LanguageGuide/OptionalChaining.html)
112 | * 야곰 - 스위프트 프로그래밍 3판
113 |
--------------------------------------------------------------------------------
/Swift/map들.md:
--------------------------------------------------------------------------------
1 | ## Map, FlatMap, CompactMap
2 |
3 | https://jinshine.github.io/2018/12/14/Swift/22.고차함수(2) - map, flatMap, compactMap/
4 |
5 | https://zeddios.tistory.com/448
6 |
7 | ### map
8 |
9 | map은 배열 내부의 각 값을 mapping 해준다. 각 요소의 값을 변경하여 배열로 반환
10 |
11 | ```swift
12 | let array = [1, 2, 3, 4, 5]
13 | array.map { $0 + 1 }
14 |
15 | // [2, 3, 4, 5, 6]
16 | ```
17 |
18 | ### compactMap
19 |
20 | Swift 4.1에서 기존 flatMap의 일부 기능을 compactMap의 기능으로 바뀌고 서로 쓰임이 다르게 되었다.
21 |
22 | ```swift
23 | let array = [1, nil, 3, nil, 5]
24 | let compactMapArray = array.compactMap { $0 }
25 |
26 | // [1, 3, 5]
27 |
28 | let str: String? = "hi"
29 | let strArray = [str, nil, "hi"]
30 | let compactMapStrArray = strArray.compactMap { $0 }
31 |
32 | // ["hi", "hi"]
33 | ```
34 |
35 | 다음과 같이 1차원 배열에서 compactMap은 nil값을 제거하고, 옵셔널 바인딩 해준다.
36 |
37 | 2차원 배열에서는 ?
38 |
39 | ```swift
40 | let array2: [[Int?]] = [[1, 2, 3], [nil, 5]]
41 | let compactMapArray2 = array2.compactMap { $0 }
42 |
43 | // [[Optional(1), Optional(2), Optional(3)], [nil, Optional(5)]]
44 | ```
45 |
46 | compactMap은 2차원 배열에서 아무 소용 없어진다..
47 |
48 | ### flatMap
49 |
50 | 위와 같은 1차원 배열에 flatMap을 적용해보면
51 |
52 | ```swift
53 | let flatMapArray = array.flatMap { $0 }
54 | let flatMapStrArray = strArray.flatMap { $0 }
55 |
56 | // [1, 3, 5]
57 | // ["hi", "hi"]
58 | ```
59 |
60 | 결과는 compactMap과 동일하게 나오지만
61 |
62 |
63 |
64 | compactMap으로 쓰라고 나온다. 언제까지 1차원 배열에서 flatMap 결과를 보장할지 모르니 이런 경우에는 compactMap을 쓰도록 하자.
65 |
66 | 2차원 배열에서도 해보면
67 |
68 | ```swift
69 | let array2: [[Int?]] = [[1, 2, 3], [nil, 5]]
70 | let flatMapArray2 = array2.flatMap { $0 }
71 |
72 | // [Optional(1), Optional(2), Optional(3), nil, Optional(5)]
73 | ```
74 |
75 | 2차원이었던 배열을 1차원으로 flat하게 만들어주지만, nil을 제거해 주지 않고 옵셔널 바인딩도 수행하지 않는다.
76 |
77 | 즉, 단순히 2차원 배열을 1차원으로만! 만들어줌
78 |
79 | ```swift
80 | let arrayNonOptional: [[Int]] = [[1, 2, 3]]
81 | let flatMapArrayNonOptional = arrayNonOptional.flatMap { $0 }
82 |
83 | // [1, 2, 3]
84 | ```
85 |
86 | optional이 아닐 때에도 2차원을 1차원 배열로만 바꿔주는 역할만 한다.
--------------------------------------------------------------------------------
/Swift/특징.md:
--------------------------------------------------------------------------------
1 | ### About Swift
2 |
3 | ##### Q. Swift의 'safe' 특성이 무엇이고 어떤 상황에서 그러한 특성이 발휘되는지 예시를 들어 설명해주세요
4 |
5 | https://swift.org/about/#swiftorg-and-open-source
6 |
7 | 1. **Safe**
8 |
9 | 코드는 안전한 방식으로 쓰여져야 한다. 명확하지 않은 습성은 안정성의 적이며, 개발자의 실수는 소프트웨어가 생산되기 전에 발견되어야 한다. swift는 때로 엄격하다고 느껴질 수 있지만, 명확성이야말로 장기적으로 시간을 아낀다고 생각한다.
10 |
11 | 변수들은 사용되기 전에 항상 초기화 되어야 하고, 배열이나 정수는 overflow가 체크되어야 하며 메모리는 자동으로 관리된다. 문법들은 당신의 의도를 쉽게 결정할 수 있도록 한다. 예) var/let
12 |
13 | swift 객체는 기본적으로 nil이 될 수 없으며, nil 객체를 만들거나 사용하려고 하면 컴파일 오류가 발생한다. 이것은 코드를 더욱 명확하고 안전하게 하고, 런타임 오류를 방지한다. 그러나 nil이 필요할 때를 위해 swift는 **optional**을 제공한다.
14 |
15 | optional은 nil을 포함할 수 있지만, ?를 사용하도록 강제하여 컴파일러에게 당신이 이 객체의 동작을 이해하고 안전하게 다룰 것이라는 것을 알려준다.
16 |
17 | 2. **Fast**
18 |
19 | swift는 c기반의 언어를 대체하려는 의도를 가지고 있다. 따라서 swift는 대부분의 작업에서 해당 언어와 비슷한 성능을 가져야한다. 성능은 예측 가능하고, 일관적이어야 한다. 훌륭한 언어들이 있지만 빠른 것은 흔치 않다.
20 |
21 | 3. **Expressive**
22 |
23 | 사용하기 편하고 보기 좋은 문법 구현
--------------------------------------------------------------------------------
/iOS/App&SceneDelegate.md:
--------------------------------------------------------------------------------
1 |
2 | ## AppDelegate / SceneDelegate과 관련된 면접 질문
3 |
4 | * SceneDelegate에 대해 설명하시오. [출처](https://github.com/JeaSungLEE/iOSInterviewquestions#ios)
5 | * 상태 변화에 따라 다른 동작을 처리하기 위한 AppDelegate methods를 설명하시오. [출처](https://github.com/JeaSungLEE/iOSInterviewquestions#ios)
6 | * 상태 변화에 따라 다른 동작은 iOS13 이후부터 SceneDelegate의 역할이 되었음
7 |
8 | ## iOS 12 이하 버전의 AppDelegate
9 |
10 | Application은 **1개의 process** 와 **1개의 user interface 인스턴스** 를 가졌다.
11 | AppDelegate은 2가지 역할을 했는데, application에게
12 |
13 | 1. process level의 이벤트 발생을 알려주었고
14 | 2. UI의 상태변화를 알려주었다.
15 |
16 |
17 |
18 | process level의 이벤트의 예시로는 application이 launch 되고 있는지 또는 terminate 되기 직전 등이 있고,
19 | UI의 상태변화는 willEnterForeground, willResignActive와 같은 메소드들을 통해 알릴 수 있었다.
20 |
21 | ## iOS 13 이후 버전의 AppDelegate
22 |
23 | 그러나 iOS 13으로 넘어오면서 **1 process & multiple user interface(= scene sessions)** 를 지원하게 되었다.
24 | 실제로 plist의 Application Scene Manifest를 보면 기존에는 없던 Enable Multiple Windows가 포함되어 있다.
25 | 기존에는 AppDelegate에서 UIWindow 객체에 대한 configuration도 진행했었는데 이제는 하나의 window 객체만 관리하지 못한다.
26 | AppDelegate의 일부 역할을 SceneDelegate에게 넘겨주었고, AppDelegate은 새로운 역할을 하나 더 맡게 되었다.
27 |
28 | 1. process level의 이벤트 발생을 알려주고 (그대로)
29 | 2. session life-cycle을 application에게 알려주게 되었다. (신규)
30 |
31 |
32 |
33 | 기존의 UI 상태 변화를 알려주는 것은 SceneDelegate이 책임지게 되었고 AppDelegate은 새로운 Scene Session이 생성되거나 버려질 때 알리는 역할을 새로 맡게 되었다.
34 |
35 | ## SceneDelegate
36 |
37 | **UI의 상태변화를 메소드들을 통해 application에게 알리는 역할**을 한다. 기존 AppDelegate들이 갖고 있던 메소드들과 거의 1:1 mapping이 가능하다.
38 | SceneDelegate은 아래의 메소드를 갖고 있다.
39 | * ```scene(_: willConnectTo: options:)```
40 | * ```sceneWillEnterForeground(_ :)```
41 | * ```sceneDidBecomeActive(_ :)```
42 | * ```sceneWillResignActive(_ :)```
43 | * ```sceneDidEnterBackground(_ :)```
44 | * ```sceneDidDisconnect(_ :)```
45 |
46 | ### 실제 call stack 따라가보기
47 |
48 | 1. AppDelegate: didFinishLaunching → UI와 무관한 일회성 setup을 이곳에서 해도 무방하다
49 | 2. system이 scene session을 생성했다. (UI Scene 아님)
50 | 3. AppDelegate: UISceneConfiguration
51 | 4. SceneDelegate: scene willConnectTo
52 |
53 | **User가 Home 화면으로 돌아갔다**
54 |
55 | 1. SceneDelegate: willResignActive
56 | 2. SceneDelegate: didEnterBackground
57 | 3. SceneDelegate: didDisconnect
58 |
59 | > **didDisconnect** 살펴보기
60 | > system은 한정 된 자원을 가지고 있기 때문에 자원을 언젠가는 회수를 해야 한다. 따라서 scene이 background에 들어간 후 어느 시점에 scene을 memory로부터 해제시킨다. 즉,
61 | > 1. scene delegate이 메모리에서 해제됨
62 | > 2. scene delegate이 관리하던 window 및 view 계층 메모리에서 해제됨
63 |
64 | > 따라서 didDisconnect코드 내부에서는 scene과 관련된 불필요한 자원을 돌려주는 작업을 해주면 된다. 가령 디스크나 네트워크를 통해 쉽게 데이터를 다시 불러올 수 있다거나
65 | 재생성이 쉬운 데이터는 돌려주는게 좋지만 **scene이 다시 reconnect** 될 수 있으므로 사용자의 input과 같이 재생성이 어려운 데이터는 가지고 있어야 하니 데이터를 무조건적으로 영구삭제하면 안된다.
66 |
67 | **User가 app switcher틑 통해 app 종료**
68 |
69 | 1. AppDelegate: didDiscardSceneSessions → 이 곳에서 user data, state 등 scene과 관련된 모든 데이터를 지우는 작업을 해주면 된다.
70 |
71 | ## Reference
72 |
73 | * [WWDC - Architecting Your App for Multiple Windows](https://developer.apple.com/videos/play/wwdc2019/258/)
74 | * [공식문서](https://developer.apple.com/documentation/uikit/uiapplicationdelegate)
75 | * [DW 블로그](https://www.donnywals.com/understanding-the-ios-13-scene-delegate/)
76 |
--------------------------------------------------------------------------------
/iOS/CoreData-migration.md:
--------------------------------------------------------------------------------
1 | ### CoreData Migration
2 |
3 | ### Q.코어데이터를 사용할 때 어려움이 있었다고 했는데 어트리뷰트 타입이 변경될 경우 이전 버전과 매핑이 필요한 이유는 무엇일까요?
4 |
5 | https://developer.apple.com/documentation/coredata
6 |
7 | 코어데이터는 앱의 데이터 모델의 최신화를 유지하기 위해서 migration tool을 제공합니다.
8 |
9 | 만약 migration 작업을 수행하지 않으면, 앱의 데이터 모델의 변경이 있었을 때 기존 앱을 사용하던 사용자는 기존 persistant container에 저장된 모델 데이터와 새로운 데이터 간의 충돌이 일어나게 되고 앱이 크래시 납니다.
10 |
11 | 따라서 꼭 migration 작업이 필요합니다.
12 |
13 | migration에는 크게 두가지가 있는데, 데이터 모델의 변경 정도가 복잡하지 않아 자동적으로 migration이 가능한 lightweight migration과 lightweight migration의 범주를 벗어나 직접 mapping을 시켜줘야 하는 heavyweight migration이 있습니다.
14 |
15 | lightweight migration 같은 경우는
16 |
17 | 1. 새로운 엔티티, 애트리뷰트, 릴레이션십이 추가되거나
18 |
19 | 2. 기존 엔티티, 애트리뷰트, 릴레이션십이 제거되거나
20 |
21 | 3. 논옵셔널 타입을 옵셔널 타입으로 변경하거나
22 |
23 | 등의 상황에서 사용 가능 합니다
24 |
25 | 저희 팀의 경우는 Int 타입이었던 애트리뷰트를 string 타입으로 변경해야 했기 때문에
26 |
27 | `NSEntityMigrationPolicy` 을 채택한 custom migrationPolicy 객체를 만들어 직접 mapping 작업을 진행해 주었습니다.
28 |
29 | ```swift
30 | class POITransformationPolicy: NSEntityMigrationPolicy {
31 |
32 | override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
33 |
34 | let POI = NSEntityDescription.insertNewObject(forEntityName: "POI", into: manager.destinationContext)
35 |
36 | let id = sInstance.value(forKey: "id") as? Int64 ?? 0
37 | let stringId = String(id)
38 |
39 | POI.setValue(stringId, forKey: "id")
40 | POI.setValue(sInstance.value(forKey: "lat"), forKey: "lat")
41 | POI.setValue(sInstance.value(forKey: "lng"), forKey: "lng")
42 |
43 | manager.associate(sourceInstance: sInstance, withDestinationInstance: POI, for: mapping)
44 | }
45 |
46 | }
47 | ```
48 |
49 |
--------------------------------------------------------------------------------
/iOS/ImageDownload:Cache.md:
--------------------------------------------------------------------------------
1 | ## Image Cache (NSCache/ FileManager)
2 |
3 | https://nsios.tistory.com/58
4 |
5 | 이미지 캐시에는 두 가지 방법이 있다.
6 |
7 | ### 1. NSCache
8 |
9 | 메모리 캐시, 즉 기기를 끄면 사라진다
10 |
11 | ### 2. FileManager
12 |
13 | 디스크 캐시, 기기 안에 저장되어있고, 껐다 켜도 남아있음.
14 |
15 |
16 |
17 | ### 이미지 캐시 과정
18 |
19 | 1. 이미지가 memory cache에 있는지 확인
20 | 2. 이미지가 disk cache 확인, 이미지가 존재하면 memory cache에 올려둘 수 있음
21 | 3. 다 없으면 서버 통신을 통해 이미지 다운로드
22 |
23 | ------
24 |
25 | ## URLSessionDataTask vs URLSessionDownloadTask
26 |
27 | https://stackoverflow.com/questions/20604910/what-is-difference-between-nsurlsessiondatatask-vs-nsurlsessiondownloadtask
28 |
29 | - dataTask
30 |
31 | 메모리에 올라감, NSData 객체를 사용하여 데이터를 보내고 받는다. 데이터를 파일에 저장하지 않으므로, 백그라운드 세션에서는 지원되지 않는다.
32 |
33 | - downloadTask
34 |
35 | 응답 받은 데이터를 임시 파일에 직접 쓴다. 앱이 실행되고 있지 않을 때 즉, 백그라운드에서 다운로드를 지원한다. 데이터를 파일 형식으로 얻음.
36 |
37 |
38 |
39 | ### +)
40 |
41 |
42 |
43 | 다음 함수는 동기 함수이기 때문에 되도록 사용을 지양하자!!
44 |
45 |
--------------------------------------------------------------------------------
/iOS/MainRunLoop.md:
--------------------------------------------------------------------------------
1 | ## Main run Loop
2 |
3 | ### UIApplication
4 |
5 | UIApplication.shared 형태의 싱글턴으로 접근 가능하다. 유저의 이벤트에 반응하여 앱의 초기 설정을 하는 역할. Background에 진입한 상태에서 추가적인 작업을 할 수 있도록 만들어주거나, 푸시알람을 받았을 때 어떤 방식으로 이를 처리할 지 등에 관한 것을 다룬다.
6 |
7 |
8 |
9 | ### Main Run Loop
10 |
11 | UIApplication 객체는 앱이 실행될 때, Main Run Loop를 실행한다. Main Run Loop는 유저가 이벤트를 일으키는 이벤트들을 처리하는 프로세스이다. view와 관련된 이벤트나 view의 업데이트에 활용, view와 관련되어 있기 때문에 main 쓰레드에서 실행된다.
12 |
13 | - 이벤트 발생 과정
14 | 1. 이벤트 발생 (터치, 줌)
15 | 2. 시스템을 통해 이벤트 생성
16 | 3. UIKit 프레임워크를 통해 생성된 port로 해당 이벤트가 앱으로 전달
17 | 4. 이벤트는 앱 내부적으로 queue의 형태로 정리되고, Main Run Loop에하나씩 mapping
18 | 5. UIApplication 객체는 가장 먼저 이벤트를 받는 객체로 어떤 것이 실행되야 하는지 결정
--------------------------------------------------------------------------------
/iOS/RenderingProcess.md:
--------------------------------------------------------------------------------
1 | ## iOS Rendering Process
2 |
3 | https://medium.com/@duwei199714/ios-why-the-ui-need-to-be-updated-on-main-thread-fd0fef070e7f
4 |
5 | 위 글을 번역 및 해석
6 |
7 | ### Rendering Framework
8 |
9 |
10 |
11 | - UIKit: 모든 종류의 컴포넌트를 포함하며, 사용자 이벤트를 관리한다. 렌더링 코드를 포함하지 않음
12 | - CoreAnimation: 모든 뷰의 그리기, 보여주기, 애니메이션을 담당
13 | - OpenGL ES: 2D와 3D 렌더링 서버를 제공
14 | - Core Graphics: 2D 렌더링 서버 제공
15 | - Graphics Hardware: GPU
16 |
17 | 따라서 모든 뷰는 UIKit이 아니라 **Core Animation Framework에서 보여지고 애니메이트 된다.**
18 |
19 |
20 |
21 | ### Core Animation Pipeline
22 |
23 |
24 |
25 | Core Animation은 렌더링 시 4단계로 나뉘어진 파이프라인을 사용한다.
26 |
27 | - **Commit Transaction**: 뷰 레이아웃, 이미지 디코딩, 포맷 변환, 뷰 레이어 패킹 및 렌더링 서버로 전송
28 | - **Render Server**: 렌더링, commit transaction에서 전송된 패키지를 분석하고 렌더링 트리로 deserialization. 그 후 뷰 레이어 프로퍼티에 따라 drawing 지시사항을 생성하여 다음 VSync Signal 때 OpenGL이 스크린을 렌더링 하도록 호출한다.
29 | - **GPU**: 스크린의 VSync Signal을 기다렸다가 렌더링을 위해 OpenGL을 사용한다. 렌더링 후 결과를 버퍼에 전송한다
30 | - **Display**: 버퍼로부터 데이터를 가져와 화면에 보여주기 위해 전송한다.
31 |
32 | 우리는 이 작업의 준비가 1/60초 안에 끝나 렌더 서버로 전송하고 렌더링 하는데에 1/60초 안에 완료되어 앱이 고착되지 않는 것을 기대한다. (not stuck)
33 |
34 | > 하지만 만약 background thread가 UI를 업데이트하게 되면 runloop가 종료되고 렌더링을 하려할 때 문제가 발생한다. 각 쓰레드가 다른 렌더링 정보를 commit하고 Core Animation Pipeline에서는 CPU에 계속해서 렌더링 정보를 commit하게 된다. 그러나 **렌더링 작업은 시스템 리소스에 비용이 많이 들고, thread 간의 컨텍스트 전환과 다량의 트랜잭션으로 인해 GPU에서 처리가 불가능하게 된다**. 결국 1/60초 내에 레이어 트리를 전송할 수 없어 성능에 영향을 미친다.
35 |
36 |
37 |
38 | #### 그래도 background threads에서 UI를 업데이트 하고 싶다면,,
39 |
40 | thread-safe한 객체를 사용하는 `Texture`와 `ComponentKit` 과 같은 프레임워크들이 있다.
41 |
42 |
43 |
44 | ---
45 |
46 | ### What is Rendering Tree?
47 |
48 | https://gist.github.com/JeOam/94e833bcefd738d805cc
49 |
50 |
51 |
52 | #### 더 자세한 내용은
53 |
54 | https://developer.apple.com/videos/play/wwdc2011/121/
55 |
--------------------------------------------------------------------------------
/iOS/TableView.md:
--------------------------------------------------------------------------------
1 | # TableView
2 |
3 | ### **테이블 뷰에 Custom Cell 디자인해서 화면에 출력하기까지 과정을 설명해주세요.**🌟
4 |
5 | **Custom Cell 디자인**
6 |
7 | 스토리보드를 이용한다고 했을때, Xcode를 이용하여 Cell을 디자인할수 있습니다. 이때 **프로토타입 Cell**이 제공되는데, 셀을 디자인하기 위한 template역할을 합니다.
8 |
9 | 기본적으로 제공되는 스타일이 아닌 cell을 이용하고 싶으면 **UITableViewCell를 subclassing**하는 class를 만들고, outlet으로 연결해줘야합니다.
10 |
11 | 해당 Cell을 tableview가 이용하기위해서는 테이블 뷰의 tableView(_:cellForRowAt:) 메소드에서 **dequeueReusableCell(withIdentifier:for:)**를 이용해야합니다. 그래서 스토리보드에서 cell의 **Identifier를 꼭 지정해** 줘야합니다. 그리고 앞서 연결한 outlet에 값을 지정해주면됩니다.
12 |
13 | **만약 스토리보드를 사용안한다면?**
14 |
15 | 위의 흐름과 같은데 dequeueReusableCell하기 이전에 cell **register** 단계를 거쳐야합니다.
16 |
17 | **화면에 출력하기까지**
18 |
19 | 화면에 tableview가 생성되고 처음으로 안에 있는 cell들이 보여질때는 **새롭운 객체가 생성됩니다**. (함수 호출 순서는 **awakeFromNib** → tableView(_:cellForRowAt:) → tableView(_:willDisplay:forRowAt) )
20 |
21 | 이후에 테이블 뷰를 스크롤하게되면 테이블뷰의 **queue에서 cell을 가져와서** 재활용하게됩니다. (그래서 reusable). 이때 Cell의 prepareForReuse함수를 호출하게됩니다.
22 |
23 | 가끔 reusable cell을 이용하다보면 다른 cell의 데이터가 남아있는 경우가 있는데 **prepareForReuse에서 cell을 다시 리셋해주는 작업을** 해줘야합니다.
24 |
25 | **prefetch는 해봤나요?**
26 |
27 | 테이블뷰의 delegate와 datasource를 self로 지정해줬듯이 **prefetchDataSource를 self로 지정해줘야합니다**.
28 |
29 | 일반적으로 용량이 큰 **데이터를 미리 받아오기 위한 작업이 비동기적으로** 이루어집니다.
30 |
31 | 스크롤 방향이 반대로 바뀔때 tableView(_:cancelPrefetchingForRowsAt:)를 이용하여 **작업을 취소 할수도 있습니다.**
32 |
33 | - 참고
34 |
35 | 정말 간단한 코드 스니핏
36 |
37 | ```swift
38 | class MyCell: UITableViewCell {
39 | @IBOutlet weak var titleLabel: UILabel!
40 |
41 | override func awakeFromNib() {
42 | super.awakeFromNib()
43 | print("awakeFromNib")
44 | }
45 |
46 | override func prepareForReuse() {
47 | super.prepareForReuse()
48 | titleLabel.textColor = .label
49 | print("prepareForReuse")
50 | }
51 |
52 | @IBAction func didPressButton(_ sender: UIButton) {
53 | titleLabel.textColor = .red
54 | }
55 |
56 | }
57 | ```
58 |
59 | ```swift
60 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
61 | let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
62 | cell.titleLabel.text = "cell #\(indexPath.row)"
63 | return cell
64 |
65 | }
66 | ```
67 |
68 | **cell이 재사용되는 과정**
69 |
70 | 
71 |
72 | [https://jinnify.tistory.com/58](https://jinnify.tistory.com/58)
73 |
74 | [https://fluffy.es/solve-duplicated-cells/](https://fluffy.es/solve-duplicated-cells/)
75 |
76 | [https://developer.apple.com/documentation/uikit/views_and_controls/table_views/configuring_the_cells_for_your_table](https://developer.apple.com/documentation/uikit/views_and_controls/table_views/configuring_the_cells_for_your_table)
77 |
78 | ### **DiffableDataSource에 대해서 설명해주세요**🌟
79 |
80 | **DiffableDataSource**는 **snapshot**을 이용해서 collection view 혹은 table view에게 **데이터를 제공**합니다.
81 |
82 | 특징으로는 **새로운 snapshot**이 apply되면 이전에 있던 snapshot과 비교하여 데이터 삭제/추가/재정렬 등을 자동으로 적용합니다.
83 |
84 | **snapshot이 뭔가요?**
85 |
86 | 현재 데이터의 state를 나타냅니다.
87 |
88 | snapshot은 section과 item으로 구성되어있으며, 각각 Hashable protocol을 채택해야합니다. 일반적으로 UUID를 id값으로 줍니다. section과 같은 경우에는 enum을 쓰는경우도 있습니다.
89 |
90 | **DiffableDataSource를 썼을 때 장점이 뭔가요**
91 |
92 | 크게 두가지 이점이 있는데, 애니메이션이 자동으로 적용된다는 점, 그리고 안전하다는점이 있습니다.
93 |
94 | 첫번째는 데이터가 바뀔때 화면 변화 애니메이션이 자동적으로 적용됩니다. 이전에는 데이터에서 변화가 생기면 reloadData()을 호출해줘야했는데, 이때는 애니메이션이 없습니다.
95 |
96 | 두번째로는 더 안전하다는것입니다. 이전에는 performBatchUpdates() 를 이용할때 뷰와 데이터간의 동기화가 제대로 안되어있으면 오류가 났습니다. DiffableDataSource는 source of truth 가 snapshot 하나기이때문에 동기화에 대해서 따로 신경쓰지 않아도 됩니다.
97 |
98 | - 참고
99 |
100 | ```swift
101 | typealias DataSource = UICollectionViewDiffableDataSource
102 | typealias Snapshot = NSDiffableDataSourceSnapshot
103 |
104 | func makeDataSource() -> DataSource {
105 | // 1
106 | let dataSource = DataSource(
107 | collectionView: collectionView,
108 | cellProvider: { (collectionView, indexPath, video) ->
109 | UICollectionViewCell? in
110 | // 2
111 | let cell = collectionView.dequeueReusableCell(
112 | withReuseIdentifier: "VideoCollectionViewCell",
113 | for: indexPath) as? VideoCollectionViewCell
114 | cell?.video = video
115 | return cell
116 | })
117 | }
118 |
119 | func applySnapshot(animatingDifferences: Bool = true) {
120 | var snapshot = Snapshot()
121 | snapshot.appendSections(sections)
122 | sections.forEach { section in
123 | snapshot.appendItems(section.videos, toSection: section)
124 | }
125 | dataSource.apply(snapshot, animatingDifferences: animatingDifferences)
126 | }
127 | ```
128 |
129 | [https://developer.apple.com/documentation/uikit/nsdiffabledatasourcesnapshot](https://developer.apple.com/documentation/uikit/nsdiffabledatasourcesnapshot)
130 |
131 | `nsdiffabledatasourcesnapshot` : A representation of the state of the data in a view at a specific point in time.
132 |
133 | [https://developer.apple.com/documentation/uikit/uitableviewdiffabledatasource](https://developer.apple.com/documentation/uikit/uitableviewdiffabledatasource)
134 |
135 | - Diffable data sources **use snapshots to provide data** for collection views and table views.
136 | - It also conforms to the UITableViewDataSource protocol and provides implementations for all of the protocol’s methods.
137 |
138 | [https://www.donnywals.com/modern-table-views-with-diffable-data-sources/](https://www.donnywals.com/modern-table-views-with-diffable-data-sources/)
139 |
140 | [https://www.avanderlee.com/swift/diffable-data-sources-adoption/](https://www.avanderlee.com/swift/diffable-data-sources-adoption/)
141 |
142 | [https://medium.com/better-programming/applying-diffable-data-sources-70ce65b368e4](https://medium.com/better-programming/applying-diffable-data-sources-70ce65b368e4)
143 |
144 | For updating, both the approaches, `reloadData()` and `performBatchUpdates()`, had their own sets of issues.
145 |
146 | While `reloadData()` would ruin our chances of showing nice animations, `performBatchUpdates()` would easily lead to errors if not handled carefully.
147 |
148 | Errors like the one shown below were pretty common with `performBatchUpdates()`:
149 |
150 | ```
151 | Terminating app due to uncaught exception ‘NSInternalInconsistencyException’,
152 | reason: ‘Invalid update: invalid number of items in section 0.
153 | The number of items contained in an existing section after the update
154 | must be equal to the number of items contained in that section before.
155 | ```
156 |
157 | Gladly, Apple brought in diffable data sources to tackle such errors on our behalf.
--------------------------------------------------------------------------------
/iOS/viewLifeCycle.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## 뷰의 상태 변화 감지 메서드
4 |
5 |
6 |
7 |
8 |
9 | **뷰의 상태 변화 메서드**
10 |
11 | - ```func viewDidLoad()```
12 |
13 | - - **뷰 계층이 메모리에 로드된 직후 호출되는 메서드**
14 | - 뷰의 추가적인 초기화 작업을 하기 좋은 시점
15 | - 메모리에 처음 로딩 될때 1회 호출되는 메서드로, 메모리 경고로 뷰가 사라지지 않는 이상 다시 호출되지 않음
16 |
17 | - ```func viewWillAppear(_ animated: Bool) ```
18 |
19 | - - **뷰가 뷰 계층에 추가되고 화면이 표시되기 직전에 호출되는 메서드**
20 | - 뷰 표시와 관련하여 추가 작업을 할 수 있다
21 | - 다른 뷰로 이동했다가 되돌아오면 재호출되는 메서드로, 화면이 나타날때마다 수행해야하는 작업을 하기 좋은 시점
22 |
23 | - ```func viewDidAppear(_ animated: Bool) ```
24 |
25 | - - **뷰가 뷰 계층에 추가되어 화면이 표시되면 호출되는 메서드**
26 | - 뷰를 나타내는 것과 관련된 추가적인 작업을 하기 좋은 시점
27 |
28 | - ```func viewWillDisappear(_ animated: Bool) ```
29 |
30 | - - **뷰가 뷰 계층에서 사라지기 직전에 호출되는 메서드**
31 | - 뷰가 생성된 뒤 발생한 변화를 이전상태로 되돌리기 좋은 시점
32 |
33 | - ```func viewDidDisappear(_ animated: Bool)```
34 |
35 | - **뷰가 뷰 계층에서 사라진 후 호출되는 메서드**
36 | - 뷰를 숨기는 것과 관련된 추가적인 작업을 하기 좋은 시점
37 | - 시간이 오래 걸리는 작업은 하지 않는 것이 좋음
38 |
39 |
40 |
41 | **뷰의 레이아웃 변화 메서드**
42 |
43 | 뷰가 생성된 후 바운드 및 위치 등의 레이아웃에 변화가 발생했을 때 호출되는 메서드입니다.
44 |
45 | - ```func viewWillLayoutSubviews()```
46 |
47 | - - **뷰가 서브뷰의 레이아웃을 변경하기 직전에 호출되는 메서드**
48 | - 서브뷰의 레이아웃을 변경하기 전에 수행할 작업을 하기 좋은 시점
49 |
50 | - ```func viewDidLayoutSubviews()```
51 |
52 | - - **서브뷰의 레이아웃이 변경된 후 호출되는 메서드**
53 | - 서브뷰의 레이아웃을 변경 한 후 추가적인 작업을 수행하기 좋은 시점
54 |
55 |
56 |
57 |
58 |
59 | ### Q. 오토레이아웃에서 특정한 상황에서 제약사항을 바꾸고 싶을 때 할 수 있는 방법들을 설명해주세요
60 |
61 |
62 |
63 | * **레이아웃 변경을 위한 함수들**
64 |
65 | \- **setNeedsLayout()**
66 | \- **layoutIfNeeded()**
67 |
68 | setNeedsLayout은 다음 업데이트 사이클을 기다린 뒤 업데이트 시켜준다. 레이아웃 업데이트를 한 번에 통합해서 업데이트 해줘서 일반적으로 성능이 좋다. 바뀌는 UI가 많던가 바로 볼 필요가 없는 급한 UI가 아니면 쓰면 좋다
69 |
70 | 이 함수를 실행하면 viewwillLayoutSubviews와 viewDidLayoutSubViews도 호출
71 | viewDidLayoutSubviews에서는 스토리보드상의 레이아웃이 아니라 폰에 적용된 크기로 컴포넌트의 크기를 받아올 수 있어서 레이아웃 크기에 관련한 작업을 할 때 편함
72 |
73 | layoutIfNeeded는 즉시 업데이트 시키고 레이아웃 관련 콜백을 호출하지 않고 종료. 바로바로 변화를 보고 싶은 경우 쓰면 좋다 애니메이션 같은거에 많이 씀
74 |
75 |
76 |
77 | ## Layout Cycle
78 |
79 | https://zeddios.tistory.com/1202?category=682195
80 |
81 | Auto Layout은 view의 프레임을 즉시 업데이트 하는 대신에 가가운 미래에 layout 변경을 예약한다.
82 |
83 | layout 변경은 **layout constraints 업데이트 후, view 계층의 모든 view에 대한 프레임을 계산** 한다.
84 |
85 | `setNeedsLayout()` 혹은 `setNeedsUpdateConstraints()`를 호출하여 예약가능
86 |
87 |
88 |
89 | 위의 그림에서 layout의 constraints가 변경 될 때까지 Application Run Loop가 반복되고, 변경이 생겼을 때 Deferred Layout Pass가 예약된다.
90 |
91 |
92 |
93 | - **Constraints에 영향을 주는 요인**
94 | - constraint 활성화 혹은 비활성화
95 | - constraint 상수 값 변경
96 | - constraint의 priority 변경
97 | - view 계층에서 view 제거
98 |
99 | constarint 변경이 일어남 → 레이아웃 엔진이 레이아웃을 다시 계산 → superview에 다시 레이아웃이 필요하다고 표시 ( == `superview.setNeedsLayout()`)
100 |
101 |
102 |
103 | > Deferred Layout Pass = Update pass + Layout Pass
104 |
105 | ### Update Constraints (= Update Pass)
106 |
107 |
108 |
109 | **아래에서 위로**
110 |
111 | setNeedsUpdateConstraints() 호출 → updateViewConstraints() 호출
112 |
113 | ### Reassign View Frames (= Layout Pass)
114 |
115 |
116 |
117 | **위에서 아래로**
118 |
119 | 탐색하면서 레이아웃이 필요하다고 mark된 모든 view에서 layoutSubviews() 호출
120 |
121 | - Constraints를 수정 할 때 view 프레임이 즉시 변경될 것으로 기대하지 말자
122 | - layoutSubviews를 override 해야하는 경우, 디버깅이 어려울 수 있으므로 레이아웃 피드백 루프를 피하도록 주의 할 것
123 |
124 | https://medium.com/ios-expert-series-or-interview-series/auto-layout-cycle-in-ios-the-layout-cycle-18704d5a4ff7
125 |
126 | ### Avoiding FeedBack Loop
127 |
128 | - Do not call `setNeedsUpdateConstraints` inside your `updateConstraints` method. Calling `setNeedsUpdateConstraints` schedules another update pass, creating a feedback loop.
129 | - You must call the superclass’s implementation somewhere in your method.
130 | - You can safely invalidate the layout of views in your subtree; however, you must do this before you call the superclass’s implementation.
131 | - Don’t invalidate the layout of any views outside your subtree. This could create a feedback loop.
132 | - Don’t call `[setNeedsLayout]()` inside layoutSubviews,Calling this method creates a feedback loop.
133 | - Be careful about changing constraints. You don’t want to accidentally invalidate the layout of any views outside your subtree.
134 |
--------------------------------------------------------------------------------
/iOS/오토레이아웃과 오토리사이징.md:
--------------------------------------------------------------------------------
1 | ## Auto Layout
2 |
3 | ### 제약조건 속성
4 |
5 | 제약조건은 다음과 같이 적용된다.
6 | >Item1의 항목 = Item2의 항목 * Multiplier + Constant
7 |
8 | 제약조건의 속성으로는 다음의 세 가지가 있다.
9 | - Relation: 동일, 이상, 이하
10 | - Multiplier, Constant: 제약조건의 값
11 | - Priority: 우선순위
12 |
13 | ### 제약조건 경쟁
14 |
15 | - Relation
16 | 제약조건 간 경쟁에 영향을 미치는 요소로 관계(relation)가 있다. 예를 들어 레이블 A에 대해 중앙에 위치해야 하고, 왼쪽으로 100 포인트 이상 떨어져야한다는 제약조건이 있다고 가정해보자. 레이블 A는 크기 유지라는 제약조건이 있을 때 경쟁이 발생할 수 있다.
17 |
18 | - Priority
19 | 우선순위가 높은 것이 먼저 적용된다. 우선순위는 최대 1000까지의 자연수로 표시되며 수가 클 수록 높은 우선순위를 가진다.
20 |
21 | - 에러
22 | 동시에 만족할 수 없는 제약조건의 경우 에러를 발생시킨다. 예를 들어 오른쪽으로 80만큼 떨어지는 제약조건과 40만큼 떨어지는 제약조건이 동시에 존재하면 충돌로 인한 에러가 발생한다.
23 |
24 | ### 뷰의 컨텐츠 크기
25 | 오토레이아웃 우선순위 중 `Content Hugging Priority`와 `Content Compression Resistance Priority`이 있다.
26 |
27 | `Content Hugging Priority`은 고유 크기보다 늘어나지 않으려는 성질로 기본값은 `251`이다.
28 | `Content Compression Resistance Priority`는 고유 크기보다 작아지지 않으려는 성질로 기본값은 750이다.
29 |
30 | - [yeonduing - 레이아웃](https://yeonduing.tistory.com/29?category=836288)
31 |
32 |
33 | ## Autoresizing Mask
34 | >An integer bit mask that determines how the receiver resizes itself when its superview’s bounds change.
35 |
36 | ### Auto Layout과의 차이점
37 | 오토레이아웃과의 차이점은 오토레이아웃은 부모뷰 뿐만 아니라 형제뷰끼리의 관계도 설정할 수 있지만 오토리사이징은 부모뷰에 대해서만 관계를 설정할 수 있다는 것이다.
38 |
39 | - [apple documentation - autoresizingmask](https://developer.apple.com/documentation/uikit/uiview/1622559-autoresizingmask)
40 |
41 |
42 | ## 헷갈리기 쉬운 특징
43 |
44 | ### translatesAutoresizingMaskIntoConstraints
45 | >A Boolean value that determines whether the view’s autoresizing mask is translated into Auto Layout constraints.
46 |
47 | 인터페이스 빌더에서 뷰를 작성하면 디폴트로 `false` 이지만 코드로 작성할 경우 디폴트 값이 `true`이다. 코드로 작성하면서 `Auto Layout`을 동적으로 변환할 경우 반드시 `false`로 지정해줘야한다.
48 |
49 | 만일 이 값이 `true`이면, 해당 뷰의 오토리사이징 마스크에 의해 지정된 `constraints`를 자동으로 만든다. 뷰의 크기나 위치를 수정하고 싶으면 뷰의 `frame`, `bounds`, `center properties`를 변경하여햐 한다.
50 |
51 | 오토리사이징 마스크는 `constraints` 완전하게 만드므로 추가적인 `constraints`를 줄 수 없다. 자체적으로 오토레이아웃을 사용하기 위해서는 이 값이 `false`여야 한다.
52 |
53 | >뷰에 `constraints`를 추가했는데 이유없이 에러가 왕창 뜨면 일단 `translatesAutoresizingMaskIntoConstraints` 값이 false로 지정되었는지 확인해보자!
54 |
55 | - [apple documentation - translatesAutoresizingMaskIntoConstraints](https://developer.apple.com/documentation/uikit/uiview/1622572-translatesautoresizingmaskintoco)
56 |
57 | ### leadingAnchor vs leftAnchor
58 | >`leadingAnchor`은 상대적인 위치를 나타내고`leftAnchor`는 절대적인 위치를 의미한다.
59 |
60 | `locale`과 관련된 것인데, `left -> right`가 일반적인 진행 방향인 지역이 있고, 반대로 `right -> left`가 일반적인 진행 방향인 지역이 있다. 이 때 앱에서 `leftAnchor`를 사용하여 뷰의 위치를 지정하면 일부 지역에서는 반대로 보일 수 있다.
61 |
62 | 따라서 절대적으로 왼쪽을 표시하고 싶을 때가 아니면 `leadingAnchor`를 사용하는게 좋다. 마찬가지로 `rightAnchor` 대신 `trailingAnchor`를 가용하는 것이 좋다.
63 |
64 | - [Hacking with Swift](https://www.hackingwithswift.com/example-code/uikit/whats-the-difference-between-leading-trailing-left-and-right-anchors)
65 | - [Stack Overflow](https://stackoverflow.com/questions/32981532/difference-between-leftanchor-and-leadinganchor/32981750)
66 |
67 | ### Order of Constraints
68 | `Constraints`를 사용할 때 순서에 주의해야한다.
69 |
70 | >만일 자식뷰의 끝이 부모뷰 끝에서 40만큼 떨어져있기를 원한다면 어떻게 해야할까?
71 |
72 | ```swift
73 | childView.bottomAnchor.constraints(
74 | equalTo: parentView.safeAreaLayoutGuide.bottomAnchor,
75 | constant: 40
76 | )
77 | ```
78 |
79 | 이렇게 쓰기 쉬운데 제약조건은 `childeView.bottomAnchor = parentView.bottomAnchor + 40`이 되므로 화면을 벗어나게 된다. `40` 대신 `-40`을 사용하든지 `childView`와 `parentView`의 순서를 바꿔야한다.
80 |
81 | ```swift
82 | childView.bottomAnchor.constraints(
83 | equalTo: parentView.safeAreaLayoutGuide.bottomAnchor,
84 | constant: -40
85 | )
86 | ```
87 |
88 | ```swift
89 | parentView.bottomAnchor.constraints(
90 | equalTo: childView.safeAreaLayoutGuide.bottomAnchor,
91 | constant: 40
92 | )
93 | ```
94 |
--------------------------------------------------------------------------------
/개발 상식/http메시지.md:
--------------------------------------------------------------------------------
1 | # Http 메시지 분석
2 |
3 | https://sabarada.tistory.com/141
4 |
5 | ## http 통신의 구성
6 |
7 | 요청 - 응답 세트
8 |
9 | 시작줄, 헤더, 바디로 구성되어 있다
10 |
11 | ## 시작 줄(Start Line)
12 |
13 | 요청 시작줄은 서버에 무엇을 해야할지, 응답 시작줄은 그 결과가 어땠는지 알려줌
14 |
15 | ### 요청
16 |
17 | ```swift
18 | // 요청 시작줄
19 | GET / HTTP/1.1
20 | ```
21 |
22 | - 메서드
23 |
24 | 제일 처음 나오는 부분으로 서버에 어떤 행동을 하라는 것을 요청하는지 명시하는 부분. GET: 읽기, POST: 쓰기, PUT: 업데이트, DELETE: 삭제
25 |
26 | - 요청 URL
27 |
28 | 요청하는 URL path를 명시. 전체 url, 상대 url을 사용할 수 있으며 상대 url을 사용할 때는 base URL을 host 헤더에 명시해 주어야한다
29 |
30 | - Http 버전 번호
31 |
32 | 요청과 응답에 모두 명시. http 프로토콜 버전을 알려줌
33 |
34 | ### 응답
35 |
36 | ```swift
37 | HTTP/1.1 200 OK
38 | ```
39 |
40 | - Http 버전 번호
41 |
42 | - 상태 코드(status code)
43 |
44 | 클라이언트에게 서버로 요청한 일이 어떻게 마무리되었는지 알려주는 부분.
45 |
46 |
47 |
48 | 404 Error : 사용자가 요청한 페이지나 파일을 찾을 수 없을 때. 보통 페이지가 이동되거나 삭제된 경우
49 |
50 | - 사유 구절(reason-phrase)
51 |
52 | 숫자로 된 상태 코드를 설명해주는 짧은 문구.
53 |
54 | ## 헤더(headers)
55 |
56 | 헤더 필드는 메세지에 추가적인 정보를 더해준다.
57 |
58 | **[일반 헤더]**
59 |
60 | 요청 메시지와 응답 메시지 모두 사용되는 헤더
61 |
62 | - connectioin
63 |
64 | 클라이언트와 서버의 연결을 지속할 것인지 마무리 할 것인지에 관한 정보
65 |
66 | - date
67 |
68 | 메시지 생성 날짜와 시간
69 |
70 | - via
71 |
72 | 메시지가 어떤 proxy, gateway를 통해 전달됐는지 명시
73 |
74 | proxy: 클라이언트와 서버 사이의 중계기. 일부는 서버에 요청된 내용을 캐시해두기도 한다. gateway: 서로 다른 통신망, 프로토콜을 사용하는 네트워크 간의 통신을 가능하게 한다. 다른 네트워크로 들어가는 네트워크 포인트. 서로 다른 네트워크 상의 통신 프로토콜을 적절히 변환해주는 역할을 한다.
75 |
76 | ## 본문(body)
77 |
78 | http 메시지가 가지고 있는 실제 구체적 내용. text, json 메시지 뿐만 아니라 이미지, 비디오, HTML 문서 등 다양한 형식이 들어올 수 있다.
79 |
80 |
--------------------------------------------------------------------------------
/개발 상식/프레임워크와라이브러리.md:
--------------------------------------------------------------------------------
1 | # 프레임워크와 라이브러리의 차이점
2 |
3 | [프레임워크와 라이브러리의 차이점](https://webclub.tistory.com/458)
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. 프레임워크의 event, delegate에 나의 메소드를 등록하는 방법
33 | 2. 프레임워크에 정의되어 있는 인터페이스, 추상타입을 나의 코드에서 구현, 상속 한 후 프레임워크에 넘겨주는 방법
34 |
35 | 이는 객체를 프레임워크에 주입하는 것이고 이를 의존성 주입 `dependency injection`이라고 한다.
36 |
37 |
--------------------------------------------------------------------------------