본문 바로가기

iOS

[iOS] DispatchQueue로 비동기 프로그래밍

DispatchQueue

작업항목의 실행을 관리하는 클래스

Queue에 추가된 작업항목은 시스템이 관리하는 스레드 풀에서 처리하고, 작업을 완료하면 스레드를 알아서 제거

일반 스레드 코드보다 쉽고, 효율적으로 코드를 작성할 수 있음

서버에서 데이터를 내려받을 때, 이미지나 동영상 등의 멀티미디어 처리와 같이 CPU 사용량이 많은 처리를 별도의 스레드에서 처리

➡️ 메인 스레드로 결과를 전달해 화면에 표시

DispatchQueue 생성시 Serial이 기본, attribute를 따로 주면 Concurrent 유형으로 바꿀 수 있음 

 

+

OperationQueue: Operation을 OperationQueue에 제출하면 Concurrent operation 객체를 만들어 줌

 


DispatchQueue로 비동기 프로그래밍

guard let url: URL = URL(string: " ~~ url ~~ ") else { return } 
 
let session: URLSession = URLSession(configuration: .default)

let dataTask: URLSessionDataTask = session.dataTask(with: url) { (data: Data?, response: URLResponse?, error: Error?) in
	if let error = error {
    	print(error.localizedDescription)
        return
    }
    
    guard let data = data else { return }
    
    do {
    	let apiResponse: APIResponse = try
    	JSONDecoder().decode(APIResponse.self, from: data)
    	self.friends = apiResponse.results

	// UI 관련 작업은 메인 스레드에서 !
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
        
    } catch(let arr) {
    	print(err.localizedDescription)
    }
}

dataTask.resume()
// 이를 호출해주면 이 때 dataTask를 실행하고 서버에 요청

URLRequest의 경우 값을 가져온 뒤 자동으로 메인 스레드로 돌아오지 않기 때문에

DispatchQueue.main.async 코드에 넣어서 메인 스레드에서 작업하도록 해 주어야 함 

 

 

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
	
    ...
    
    cell.imageView?.image = nil
    // 셀이 재사용되기 전에 셀의 이미지뷰의 이미지를 없애줘야 함
    // 이미지를 가져오기 전에 다른 셀의 이미지가 표현되지 않도록 
    // placeholder 이미지를 넣어주면 좋음 
    
    // global : 백그라운드에서 동작하는 큐
    DispatchQueue.global().async {
    	guard let imageURL: URL = URL(string: friends.picture.thumbnail) else { return }
        guard let imageData: Data = try? Data(contentsOf: imageURL) else { return }
        
        // UI 관련 작업은 메인에서 
        DispatcheQueue.main.async {
        	
            // 이미지를 다운받는 동안 화면을 움직여 셀의 Index가 바뀌면?
            // 셀이 데이터를 setting 해주고 있는 현재의 index와, 이미지 다운이 끝났을 때의 index가 달라질 수 있음
            // -> 이를 구분해주고 서로 일치하는 상황에서만 이미지를 setting 
        	if let index: IndexPath = tableView.indexPath(for: cell) {
            	if index.row == indexPath.row {
                	cell.imageView?.image = UIImage(data: imageData)
                }
    	    }
        }
    }
	return cell
}

 

DispatchQueue.main.asyncAfter : 코드를 일정 시간 뒤에 실행하고자 할 때 사용

DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
	print("print")
}