Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[iOS] GCD 사용시 주의사항 #9

Closed
ujhong7 opened this issue Mar 24, 2024 · 0 comments
Closed

[iOS] GCD 사용시 주의사항 #9

ujhong7 opened this issue Mar 24, 2024 · 0 comments

Comments

@ujhong7
Copy link
Owner

ujhong7 commented Mar 24, 2024

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

메인 Thread 화면을 다시 그리는 역할 → UI관련 일들은 다시 메인쓰레드로 보낼 필요

DispatchQueue.main

  • 에러발생

    UI와 관련된 작업들은 메인쓰레드에서 처리하지 않으면 에러 발생(메인쓰레드가 아닌 쓰레드는 그림을 다시 그리지 못함)

    DispatchQueue.global().async {
    	...
    	self.textLabel.text = "New posts updated!" // ❌ Error
    }
  • 메인쓰레드

    UI와 관련된 작업들을 메인쓰레드에서 처리할 수 있도록 메인큐를 통해서, 작업을 다시 메인쓰레드로 보냄

    DispatchQueue.global().async {
    	...
    	DispatchQueue.main.async {
    		self.textLabel.text = "New posts updated!"
    	}
    }

    UI관련 일이기 때문에 그림을 다시 그리는 작업은 메인큐에서!

2️⃣ completionHandler의 존재이유 - 올바른 콜백함수의 사용

데이터를 return으로 전달하면 안되고, closure로 전달해야함!

@escaping(데이터) -> Void
  • 잘못된 함수설계

    비동기적인 작업을 해야하는 함수를 설계할때, return을 통해서 데이터를 전달하면 항상 nil이 반환

    func getImage(image: string) -> UIImage? {
    	URLSession.shared.dataTask(...) {
    	..
    	}.resume()
    	...
    	return photoImage // ⭐️ 함수 내부의 일이 끝나기전에 return 하므로 무조건 nil을 반환!
    }
  • 제대로된 함수 설계

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

    func getImage(image: @escaping (UIImage?) -> Void) {
    	...
    	URLSession.shared.dataTask(..) {
    		...
    		completion(photoImage) // ⭐️ 함수 내부의 일이 끝나면 completion 클로저 호출!
    	}.resume()
    }

위 예제에서 URLSession은 내부적으로 비동기적으로 실행되기 때문에 내부 실행을 기다리지 않고 return 으로 넘어감 → 무조건 nil로 리턴됨..

즉, return이 아닌 콜백함수를 통해 끝나는 시점을 알려줘야함!

3️⃣ weak, strong 캡처 주의 - 객체 내에서 비동기코드 사용 시

  • 강한참조

    캡처리스트 안에서 weak self로 선언하지 않으면 강한 참조(strong)

    1. 서로를 가리키는 경우 메모리누수 발생 가능

    2. 클로저의 수명주기가 길어지는 현상이 발생

  • 약한참조

    대부분의 경우, 캡처리스트 안에서 weak self로 선언하는 것을 권장

    DispatchQueue.global().async { [weak self] in 
    	guard let self = self else { return }
    	...
    	DispatchQueue.main.aysnc {
    		self.textLabel.text = "New posts updated!"
    	}
    }

4️⃣ 동기함수를 비동기적으로 동작하는 함수로 변형하는 방법

  • 일반(동기)함수

    오래걸리는 일반적인 함수를 단순히 동기함수로 만들면 메인쓰레드에 부하가 걸림

    func doSomething() {
    	print("시작")
    	sleep(3)
    	print("종료")
    }
    
    print("1")
    doSomething()
    print("2")
    
    // 1
    // 시작
    // 종료
    // 2
  • 비동기함수

    오래걸리는 일반적인 함수를 내부에 비동기적 처리를 하면 비동기로 동작하는 함수로 변형가능

    func doSomething(com: @escaping (Void) -> Void) {
    	DispatchQueue.global().async {
    		print("시작")
    		sleep(3)
    		print("종료")
    		com()
    	}
    }
    
    print("1")
    doSomething()
    print("2")
    
    // 1
    // 2
    // 시작
    // 종료
    
    // 등등..

5️⃣ 비동기 함수/메서드의 이해

일반적으로 대부분의 네트워킹 등 오래걸리는 API들은 따로 비동기처리를 하지 않아도 내부가 비동기적으로 구현되어 있음

URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in
	self.image = UIImage(data: data!)
}

내부가 비동적으로 처리 되어있지 않아, 디스패치큐로 클로저를 보내서 명시적으로 비동기적 처리가 필요한 API들도 있음

DispatchQueue.global().async {
	... 
	let imageData = try? Data(contentsof: url)!
	let someImage = UIImage(data: imageData)!
	
	DispatchQueue.main.async {
		self.imageView.image = someImage
	}
}
@ujhong7 ujhong7 closed this as completed Mar 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant