ios에서 업데이트 사이클(이벤트 결과를 화면에 출력) 작업을 수행할 수 있는 스레드는 오직 1번쓰레드 (main thread) 뿐입니다.
때문에 다른 스레드에서 작업을 수행하던 중 UI 관련 작업이 시작되면 , 해당 작업을 메인 큐를 통해 1번 쓰레드로 보내야 한다.
즉 UI관련 작업은 1번 쓰레드에서만 할 수 있으며 다른쓰레드에서 UI 작업을 만나면 1번 쓰레드로 보내야 한다.
DispatchQueue.global().async {
// 다양한 작업(UI 제외)
DispatchQueue.main.async {
//UI 작업
}
}
ios에서 비동기(Async) 처리 방식은 "해당 작업을 기다리지 않고 다음 작업을 진행하는 방식"
이러한 방식은 작업을 분산처리하여 성능을 높인다는 장점을 가지고 있지만 특정 작업의 함수 결과물을 의존/ 사용하는 다른 작업이 존재할 경우 에러가 발생할 수 있다.
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("김철수")
*/
객체 내에서 비동기 코드를 사용할 때는 약한/ 강한 참조를 생각하면서 코드를 작성해야 한다.
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() // 김철수 메모리에서 제거되었습니다.
작업 시간이 긴 함수들을 동기 함수로 만들면 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)
}