본문 바로가기

IT/iOS

iOS Concurrency 프로그래밍, 동기/비동기 처리, GCD/Operation 에 대한 이해 (3)

1) 반드시 메인큐에서 처리해야하는 작업

★ 중요하게 알아야할 한가지 개념 ★

  • UI 관련일들은 “메인큐”에서 처리해야 한다. → 화면을 그리는 일
  • UI 업데이트가 어떻게 이뤄지는지 아래 개략도를 보면 된다.
  • 모든 OS에서 화면을 업데이트하거나 표시하는 UI 작업을 하나의 쓰레드에서만 작업해야하는 특징이 있어서 메인 스레드에서 처리해야한다. → 애플에서 왜 이렇게 만들었는지 확인할 필요가 있음

2) sync 메서드에 대한 주의사항

  • sync 메서드와 관련해 절대 해서는 안되는 코드 2가지가 있다.

1. 메인큐에서는 다른큐로 작업을 보낼때 sync 메서드를 부르면 절대 안된다.

  • 메인큐에서는 항상 비동기적으로 보내야한다.
  • 즉, UI와 관련되지 않은 오래 걸리는 작업(네트워크)들은 다른 쓰레드에서 일을 할 수 있도록 “비동기적(async)”으로 실행하여야 하며, 동기적으로 시키면 UI가 멈춰서 유저한테 반응을 늦게 하고 버벅거린다.

2. 현재의 큐에서 현재의 큐로 “동기적으로” 보내서는 안된다.

  • 현재의 큐를 블락하는 동시에 다시 현재의 큐에 접근하기 때문에 교착상황(DeadLock)이 발생한다.

3) weak, strong 캡처 주의

4) 컴플리션 핸들러의 존재 이유

  • 비동기로 작업을 시키고 나서, 작업에 해당하는 값을 바로 사용하면 안된다.
  • 작업이 아직 종료하지 않았는데, 해당 값에 접근하면, 잘못된 값을 사용할 확률이 높다.
  • 그래서, 해당 비동기 작업이 끝났다는 것을 정확히 알려주는 시점이 컴플리션 핸들러 이다.
  • 비동기 함수와 관련된 작업들은 모두 컴플리션 핸들러를 가지고 있다.

5) 동기적 함수를 비동기 함수처럼 만드는 방법

  • 동기적 함수를 비동기 함수처럼 만드는 이유는 여러번 재활용을 하기 위해서이다.

6) 기타

  • URLSession과 같은 이미 비동기 함수는 GCD/Operation이 필요없다. → 이미 내장되어 있음

7) weak self와 관련된 상세 내용

DispatchQueue.global().async { [weak self] in
	DispatchQueue.main.async {

	}
} 
  • 위의 경우에는 맨위 클로저에 weak self 를 선언해주면 밑에 클로저에도 적용이 된다.
  • 이를 이해하기 위해서는 ARC와 클로저의 캡처 리스트에 대한 내용을 이해해야한다.
  • 코드를 작성할 경우 각 코드들이 메모리에 어떻게 올라가는지 알고 있어야 한다.
    • 클래스, 클로저 → 힙영역 저장
    • 구조체, enum, 자료형 (구조체로 만들어짐), 함수 → 스택 영역
    • 강한참조, 약한참조
  • 위 내용을 이해하기 위해서 아래와 같은 예제를 들었다.
class viewController: UIViewController {
	var name: String = "뷰컨"

	func doSomething() {
		DispatchQueue.global().async {
			sleep(3)
			print("글로벌큐에서 출력하기: \(self.name)")
			DispatchQueue.main.async {
				print("메인큐에서 출력하기: \(self.name)")
			}
		}
	}
	
	deinit {
		print("\(name) 메모리 해제")
	}
}

func localScopeFunction() {
	let vc = ViewController()
	vc.doSomething()
}

localScopeFunction()

 

  • 위 코드가 메모리에서 작동하는 과정은 아래와 같다.
  1. localScopeFunction() 작동 → vc 객체 생성 → ViewController ARC == 1 (+1)
  2. vc 내부 doSomething() 작동
  3. doSomething() 내부 클로저가 self 메타타입을 통해서 ViewController 객체를 가리키면서 ViewController ARC == 2 (+1)가 된다.
  4. doSomething() 함수가 종료되면서 localScopeFunction() 함수도 종료되면서 vc 객체가 사라지면서 ViewController의 ARC == 1 (-1)이 된다.
  5. 그러나 여기서 문제점이 클로저 내부의 작업들이 다른 스레드에서 동작하고 있게 되면서 vc 객체가 사라졌는데도 ViewController의 ARC == 1로 남아있게 된다.
  6. 위와 같은 문제점을 해결하기 위해 weak self 를 선언해줘서 self를 약한 참조하도록 만들어준다.