Dispatch Queue 사용시 주의 사항

JIN·2023년 1월 26일
0
post-thumbnail

1. 반드시 메인 큐에서 처리해야 하는 작업 (UI관련 작업)

ios에서 업데이트 사이클(이벤트 결과를 화면에 출력) 작업을 수행할 수 있는 스레드는 오직 1번쓰레드 (main thread) 뿐입니다.
때문에 다른 스레드에서 작업을 수행하던 중 UI 관련 작업이 시작되면 , 해당 작업을 메인 큐를 통해 1번 쓰레드로 보내야 한다.
즉 UI관련 작업은 1번 쓰레드에서만 할 수 있으며 다른쓰레드에서 UI 작업을 만나면 1번 쓰레드로 보내야 한다.

DispatchQueue.global().async {
// 다양한 작업(UI 제외)
  DispatchQueue.main.async {
  //UI 작업
  }
}

UI 관련 작업을 다른 스레드에서 작업(UI 작업 작동 X)

UI 관련 작업을 1번 쓰레드(main thread)에서 작업(UI 작업 작동O)

2. 컴플리션 핸들러의 존재 이유 - 올바른 콜백 함수의 사용

ios에서 비동기(Async) 처리 방식은 "해당 작업을 기다리지 않고 다음 작업을 진행하는 방식"
이러한 방식은 작업을 분산처리하여 성능을 높인다는 장점을 가지고 있지만 특정 작업의 함수 결과물을 의존/ 사용하는 다른 작업이 존재할 경우 에러가 발생할 수 있다.

  • 서로 다른 스레드에서 task1과 task2가 동시에 진행하려면 에러가 발생
    (task2의 작업 진행은 task1의 결과물에 의존하기때문)
  • 이처럼 특정 함수의 작업 결과를 의존하는 다른 함수가 존재할때는 특정 함수의 작업 결과를 반환(return)하는 방식으로 설계하면 안된다.
  • 에러(nil) 없는 작동을 위해 작업 결과를 반환(return) 하지 않는 형태 (Void) + 클로저를 호출할 수 있는 형태로 설계해야한다.
    즉 비동기 작업의 끝나는 시점을 파악하여 해당 작업 결과를 클로저로 전달하는 형태로 설계해야 한다.

    비동기적인 작업을 해야 하는 함수를 설계할 때 return을 통해서 데이터를 전달하려면 항상 nil이 반환됩니다.
var resultName: String?

func myName(name: String) -> String?{  // n번 쓰레드에서 작업
    DispatchQueue.global().async {
        sleep(2)
        resultName = name
    }
    return resultName
}

print(myName(name: "김철수")) // print() 함수는 n번 쓰레드의 함수 결과를 사용
/* 출력 결과
 nil  // myName() 함수의 작업이 끝나기 전에 print() 함수의 작업이 진행하기 때문에 nil(에러) 출력
 */

비동기적인 작업을 해야 하는 함수는 항상 클로저를 호출할 수 있도록 함수를 설계해야 합니다.

var resultName: String?

func myName(name: String, completionHandler: @escaping (String?) -> Void){  // n번 쓰레드에서 작업
   DispatchQueue.global().async {
       sleep(2)
       resultName = name
       completionHandler(resultName)
   }
}

myName(name: "김철수") { XXX in
   print(XXX)
}

/* 출력 결과
Optional("김철수")
*/

3. weak, strong 캡처의 주의

객체 내에서 비동기 코드를 사용할 때는 약한/ 강한 참조를 생각하면서 코드를 작성해야 한다.

class Man{
    var name: String
    var run: (()->Void)?
  
    init(name: String){
        self.name = name
    }
    func runClosure(){
        DispatchQueue.global().async {
            self.run = {
                print("\(self.name)이 달리고 있습니다.")
            }
        }
    }
  
    deinit{
        print("\(self.name) 메모리에서 제거되었습니다.")
    }
}

func doSomething(){
    var kim: Man? = Man(name: "김철수")  // kim 인스턴스 생성 (kim RC 1증가)
    kim?.runClosure()  // 클로저(run)가 메모리의 Heap 영역에 생성
}

doSomething()  // 아무 출력 없음

대부분의 경우 캡처리스트 안에서 weak self로 선언하여 약한 참조 하게끔 하는 것을 권장한다.

class Man{
    var name: String
    var run: (()->Void)?
  
    init(name: String){
        self.name = name
    }
      
    func runClosure(){
        DispatchQueue.global().async {
            self.run = { [weak self] in
                print("\(self?.name)이 달리고 있습니다.")
            }
        }
    }
  
    deinit{
        print("\(self.name) 메모리에서 제거되었습니다.")
    }
}

func doSomething(){
    let kim: Man? = Man(name: "김철수")  // kim 인스턴스 생성 (kim RC 1증가)
    kim?.runClosure()  // 클로저(run)가 메모리의 Heap 영역에 생성
}

doSomething()  // 김철수 메모리에서 제거되었습니다.

4. 동기 (sync) 함수를 비동기(Async) 적으로 동작하는 함수로 변형

작업 시간이 긴 함수들을 동기 함수로 만들면 1번쓰레드에 과부하가 걸립니다. 이러한 이유로 작업시간이 긴 함수를 내부에 비동기적 처리를 하여 비동기로 동작하는 함수로 변형해야 합니다.

func myTask(programmingLanguage: String) -> String{
    print("수학 숙제 시작")
    sleep(2)
    print("수학 숙제 종료")
   
    print("\(programmingLanguage) 코딩 공부 시작")
    sleep(2)
    print("코딩 공부 종료")
   
    print("영어 숙제 시작")
    sleep(2)
    print("영어 숙제 종료")
   
    return "공부 종료"
}

myTask(programmingLanguage: "Swift")

동기 함수를 비동기적으로 동작하는 함수로 변형하는 함수를 생성하여 만들면 버벅임과 같은 현상을 예방할 수 있습니다.

func myTask(programmingLanguage: String) -> String{
    print("수학 숙제 시작")
    sleep(2)
    print("수학 숙제 종료")
   
    print("\(programmingLanguage) 코딩 공부 시작")
    sleep(2)
    print("코딩 공부 종료")
   
    print("영어 숙제 시작")
    sleep(2)
    print("영어 숙제 종료")
   
    return "공부 종료"
}

func asyncMyTask(programmingLanguage: String, completionHandler: @escaping (String) -> Void){
    DispatchQueue.global().async {
        let function = myTask(programmingLanguage: programmingLanguage)
        completionHandler(function)
    }
}

asyncMyTask(programmingLanguage: "Swift") { XXX in
    print(XXX)
}
profile
배우고 적용하고 개선하기

0개의 댓글