Apr 29, 2021, TIL (Today I Learned) - DispatchQueue, Operation, 관련면접질문, 설계

Inwoo Hwang·2021년 8월 26일
0
post-thumbnail

수업 내용


비동기 처리를 왜 해야할까요? 라는 질문을 받는다면...

  1. 메인 쓰레드는 1/60초에 한번씩 (60Hz)화면을 다시 그리는 역할을 하고 있기 때문에 오래걸리는 작업(네트워크 통신)과 같은 일을 시키면 안된다. (버벅임의 원인이 된다.)
  2. iOS에서는 다른 쓰레드로 보내는 비동기처리를 큐(대기열)의 개념으로 처리한다.
  3. GCD(DispatchQueue)와 OperationQueue가 그것을 담당한다.
  4. 동시성 프로그래밍은 메인쓰레드가 아닌 쓰레드에서 소프트웨어적인 쓰레드로 동시적으로 처리하는 개념이며, UI의 반응성, 최적화와 관련되어 있다.
let mainQueue = DispatchQueue.main

let userInteractiveQueue = DispatchQueue.global(qos: .userInteractive)

올바른 비동기함수의 설계

리턴(return)이 아닌 콜백함수를 통해, 끝나는 시점을 알려줘야 한다.

❌ 이렇게 설계하면 안된다.

// 이렇게 return하도록 설계하면 안된다
func getImages(with urlString: String) -> UIImage? {
    let url = URL(string: urlString)!
    
    var photoImage: UIImage? = nil
    
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        if let error = error {
            print("에러있음: \(error)")
        }
        guard let imageData = data else { return }
        
        photoImage = UIImage(data: imageData)
        
    }.resume()
    
    return photoImage
}



getImages(with: "https://bit.ly/32ps0DI")    // 무조건 nil로 리턴함 ⭐️

URLSession.shared.dataTask 는 비동기적으로 작동하기 때문에 해당 작업이 끝나기 전에 return photoImage 이 실행 될 것이고 그러면 이미지를 리턴하는 것이 아니라 nil값을 리턴한다(해당 작업이 끝나지 전에 리턴하기 때문에)

✅ 올바른 함수(메서드)의 설계 - 콜백함수의 사용법

func properlyGetImages(with urlString: String, closure: @escaping (UIImage?) -> Void) {
    let url = URL(string: urlString)!
    
    var photoImage: UIImage? = nil
    
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        if let error = error {
            print("에러있음: \(error)")
        }
        guard let imageData = data else { return }
        
        photoImage = UIImage(data: imageData)
        
        
        closure(photoImage)
        
    }.resume()
    
}

요약

  1. 메인쓰레드는 내부 OS 메커니즘에 의해 그림 다시 그리는 쓰레드로 설계되어 있으므로 UI와 관련된 작업은 다시 메인 쓰레드(DispatchQueue.main)로 보내서 처리해야 한다.
  2. 비동기적인 함수 설계시, 이제 작업의 끝나는 시점을 기다리지 않으므로 return의 개념은 사용하지 않으며 항상 콜백 함수를 이용해서 함수를 설계해야 한다. (결국, 아무리 비동기적인 함수라고 하더라도 함수 내부에서는 동기적으로 동작하고(순서대로 작업) 모든 함수 내부의 동작이 끝나는 순간에 콜백함수를 실행시키는 원리로 비동기적인 함수의 작업 완료 시점을 콜백함수를 실행하면서 전달한다.)
  3. 작업을 보낼 때, 기본적으로 클로저를 사용하므로, 객체를 오랫동안 붙잡아두는 작업이 생길 수 있고 습관적으로 캡처리스트 내에서 [weak self]를 선언해서 사용하는 것이 좋다.
  4. 함수 내부에 DispatchQueue를 사용해서 의도적으로 비동기 함수로 설계 / 사용이 가능하다.

Async/await in Swift

비동기함수를 이어서 처리하는 것(코드상의 불편함) 해결 / Swift 5.5에서 도입

학습내용


Fezz의 멋진 그림

궁금증1: CompletionBlock은 뭐고 또 어떻게 써야 하지?

thanks to @Allen

Completion block이 어떤 의미를 가진건지 알 수 있었다. 하나의 operation이 끝난다고 해서 무조건적으로 completionblock이 바로 끝에 나오는 것이 아닐 수 있다. 다른 operation과 순서가 섞일 수 있기 때문에 operation의 일부 task 를 completion block에서 처리 하는 것을 하면 안된다는 것을 깨달았다!!

고민한 내용

For문을 활용하여 여러개의 operation을 생성한 뒤 해당 작업을 queue에다 넣어주니 operation의 우선순위와 상관없이 동기적으로 작동하는 문제를 직면했다.

For문을 사용하는게 문제일까...

내일 부디 해결할 수 있기를...

profile
james, the enthusiastic developer

0개의 댓글