1. 첫 번째 학습 내용: Race Conditions

A race condition occurs when two threads access a shared variable at the same time.

The first thread reads the variable, and the second thread reads the same value from the variable. Then the first thread and second thread perform their operations on the value, and they race to see which thread can write the value last to the shared variable. The value of the thread that writes its value last is preserved, because the thread is writing over the value that the previous thread wrote.

공학 분야에서 경쟁 상태(race condition)란 둘 이상의 입력 또는 조작의 타이밍이나 순서 등이 결과값에 영향을 줄 수 있는 상태를 말한다. 입력 변화의 타이밍이나 순서가 예상과 다르게 작동하면 정상적인 결과가 나오지 않게 될 위험이 있는데 이를 경쟁 위험이라고 한다.

Race conditions and deadlocks - Visual Basic

경쟁 상태 - 위키백과, 우리 모두의 백과사전

2. 두 번째 학습 내용: deadlock

A deadlock occurs when two threads each lock a different variable at the same time and then try to lock the variable that the other thread already locked.

As a result, each thread stops executing and waits for the other thread to release the variable. Because each thread is holding the variable that the other thread wants, nothing occurs, and the threads remain deadlocked.

교착 상태(膠着狀態, 영어: deadlock)란 두 개 이상의 작업이 서로 상대방의 작업이 끝나기 만을 기다리고 있기 때문에 결과적으로 아무것도 완료되지 못하는 상태를 가리킨다. 예를 들어 하나의 사다리가 있고, 두 명의 사람이 각각 사다리의 위쪽과 아래쪽에 있다고 가정한다. 이때 아래에 있는 사람은 위로 올라 가려고 하고, 위에 있는 사람은 아래로 내려오려고 한다면, 두 사람은 서로 상대방이 사다리에서 비켜줄 때까지 하염없이 기다리고 있을 것이고 결과적으로 아무도 사다리를 내려오거나 올라가지 못하게 되듯이, 전산학에서 교착 상태란 다중 프로그래밍 환경에서 흔히 발생할 수 있는 문제이다. 이 문제를 해결하는 일반적인 방법은 아직 없는 상태이다.

교착 상태 - 위키백과, 우리 모두의 백과사전

3. 세 번째 학습 내용: 값 타입과 참조 타입

메모리 할당은 값 타입과 참조 타입의 가장 눈에 띄는 차이점입니다. 앞서 설명했듯이 값 타입과 참조 타입은 각각 메모리의 스택과 힙 영역에 할당됩니다. 스택 영역에 할당되는 것은 스택 할당(Stack allocation), 힙 영역에 할당되는 것은 힙 할당(Heap allocation)이라고 부르겠습니다.

스택 할당과 힙 할당에는 구조적인 차이가 있습니다.

  • 스택은 한 방향으로만 데이터를 넣고 빼는 단순한 구조이기 때문에 스택 포인터를 사용하여 빠르게 접근할 수 있습니다. 그래서 스택 할당은 많은 시간을 필요로 하지 않습니다.
  • 하지만 힙은 할당할 때마다 적절한 메모리 공간이 있는지 확인한 후에 할당을 처리하는 동적인 구조입니다. 이러한 과정은 스택보다 복잡하기 때문에 더 많은 오버헤드(Overhead)가 발생하게 됩니다. 그렇기 때문에 일반적으로 더 좋은 성능의 코드를 작성하기 위해서는 값 타입을 사용하는 것이 좋습니다.

오버헤드(Overhead)

어떤 처리를 하기 위해 들어가는 간접적인 처리 시간 · 메모리 등을 말한다.

예를 들어 A라는 처리를 단순하게 실행한다면 10초 걸리는데, 안전성을 고려하고 부가적인 B라는 처리를 추가한 결과 처리시간이 15초 걸렸다면, 오버헤드는 5초가 된다. 또한 이 처리 B를 개선해 B'라는 처리를 한 결과, 처리시간이 12초가 되었다면, 이 경우 오버헤드가 3초 단축되었다고 말한다

In computer science, overhead is any combination of excess or indirect computation time, memory, bandwidth, or other resources that are required to perform a specific task.

Copy-on-Write in Swift

앞서 값 타입을 복사하면 각각의 인스턴스는 유일한 복사값을 가지게 된다고 언급하였습니다. 하지만 항상 그렇지는 않습니다. 스위프트에서는 효율적으로 자원을 관리하기 위해 Copy-on-Write라는 기술을 사용하여 인스턴스의 불필요한 복사를 줄입니다.

Copy-on-Write의 기본 원리는 다음과 같습니다.

인스턴스를 복사할 때 먼저 참조를 통해 불필요한 복사를 줄이고, 수정이 발생하는 경우에만 실제로 복사를 하는 방식

줄여서 COW라고 간단하게 표현합니다. 스위프트에서는 기본적으로 Collection 타입에 COW가 구현되어 있으며, 타입에 직접 COW를 구현할 수도 있습니다.

간단한 코드를 통해 COW의 원리를 살펴보도록 하겠습니다.

var array1: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(UnsafeRawPointer(array1)) // 0x0000000100704210

var array2 = array1
print(UnsafeRawPointer(array2)) // 0x0000000100704210

array2.removeLast() // Copy-on-Write
print(UnsafeRawPointer(array1)) // 0x0000000100704210
print(UnsafeRawPointer(array2)) // 0x0000000100404e70

array1을 선언하고 array2에 array1을 할당하면 두 인스턴스의 주소값이 동일한 것을 확인할 수 있습니다. COW에 의해 복사되지 않고 참조된 것입니다. 이때 array2의 마지막 원소를 제거하는 메서드를 호출하면 수정이 발생하므로, 참조를 끊고 다른 메모리 공간을 할당합니다. 이후 array2의 주소값을 확인하면 값이 변경된 것을 확인할 수 있습니다.

Reference and Value types in Swift - 야곰닷넷

Value and Reference Types - Swift Blog

문제점/고민한점 → 해결방안

이미지를 동기로 받아오는 방식

위의 코드에서는 image를 받아오는 작업이 동기 메소드 Data(contentsOf: imageURL)이기 때문에 다운로드가 끝날 때까지 화면이 멈춰버리게 됨 🙅‍

이미지를 비동기로 받아오는 방법


출처: 부스트코스 5. 영화정보 애플리케이션 - 4. GCD - 3) DispatchQueue를 활용한 비동기 프로그래밍

  • DispatchQueue.global()은 백그라운드 아무데서나 동작하게 될 기본적으로 제공하는 큐
  • 메인 스레드로 가져가야 할 코드는 DispatchQueue.main에다가 옮겨줌
  • 다운로드 받는 동안 사용자가 화면을 위 아래로 움직일 수 있음

→ 셀의 인덱스가 바껴버림 (= 다른 위치에 가있는 셀이 될 수 있다)

셀이 데이타를 세팅해주고 있는 인덱스와

다운로드가 끝났을 때의 인덱스가 상이할 수 있기 때문에

고거를 구분해주고

서로 일치하는 상황에서만

이미지를 세팅해줘야 한다!

그래서 같을 때만 셀에다가 이미지를 세팅해달라고 처리해둔 것임

if index.row == indexPath.row

셀의 imageView의 image는 없애줘야 함

그래야지 나중에 이미지 가져오기 전에 다른 사람 이미지가 표현이 안되있는 상태로 비워줘야 함

cell.imageView?.image = nil
DispatchQueue.global().async {
                guard let imageURL: URL = URL(string: item.thumbnails.first!) else { return }
                guard let imageData: Data = try? Data(contentsOf: imageURL) else { return }
                
                DispatchQueue.main.async {
                    if let index: IndexPath = tableView.indexPath(for: cell) {
                        if index.row == indexPath.row {
                            cell.itemThumbnail.image = UIImage(data: imageData)
                        }
                    }
                }
            }

이전과 달리 이미지를 비동기로 받아오도록 바꿔줌

테이블뷰는 잘 받아오는데

똑같이 작성한 컬렉션뷰는 이미지를 못 받아오는 현상 발생... 😣

원인이 무엇일까? 🤔

아직 해결 몬함 😣

기타) mainView에서 addSubview로 테이블뷰와 컬렉션뷰 추가한 것 삭제

이미 스토리보드에서 추가해줬으므로 코드로 또 넣어줄 필요 없음

profile
iOS Developer

0개의 댓글