dispatchQueue
공식 문서 <https://developer.apple.com/documentation/dispatch/dispatchqueue>
class DispatchQueue : DispatchObject
dispatchQueue라는 객체는 메인 스레드나 백그라운드 스레드에서 업무를 직렬이나 병렬적으로 수행하도록 한다.
이제 큐에 업무들을 제출하는데 , 백그라운드 큐에 넣으면 시스템이 알아서 수행해준다. 그런데 각 태스크가 어떤 스레드에 의해 수행되는지는 알 수 없다함. (메인 스레드는 여기서 예외라는데, 그말은 우리가 무엇을 특정지어 수행하도록 할 수 있따는 건가?)
여기서 내가 할 수 있는건 동기적으로나, 비동기적으로 태스크가 수행되도록 할 수 있는데, 메인스레드에서는 동기적으로 하지 말라한다. 그러다 데드락에 걸릴 수 있다고.
위의 내용 밑으로 avoiding excessive thread creation이라고 있는데, 일단 패스. 대충 스레드들이 어떤 업무에서 블록걸리지 말라고, 디스패치큐를 너무 많이 만들지 말라는 것이다. 스레드 생성 갯수는 제한이 있으니까
dispatchGroup
<https://developer.apple.com/documentation/dispatch/dispatchgroup>
Groups allow you to aggregate a set of tasks and synchronize behaviors on the group. You attach multiple work items to a group and schedule them for asynchronous execution on the same queue or different queues. When all work items finish executing, the group executes its completion handler. You can also wait synchronously for all tasks in the group to finish executing.
비동기적으로 진행되는 작업들에 대한 completion handler를 제공하려고 있는 건가?
자주 사용되는 메소드가 enter랑 leave인데, 블록들이 그룹내에서 시작과 종료를 관리한다 보면 될거 같다.
Explicitly indicates that a block has entered the group.
Explicitly indicates that a block in the group finished executing.
여기서는 왜 이렇게 하신걸까 <https://github.com/ChangYeop-Yang/Study-iOS/blob/master/%5BiOS%5D%20Kakao%20Pay%20Subject/KakaoPaySubject/KakaoPaySubject/Location%20Files/CLocation.swift>
group.enter()
DispatchQueue.global(qos: .userInitiated).async(group: group, execute: { [unowned self] in
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemark, error) -> Void in
guard error == nil, let place = placemark?.first else {
group.leave()
fatalError("❌ Error, Convert GEO Location.")
}
if let administrativeArea: String = place.administrativeArea { self.currentAddress.append(administrativeArea + " ") }
if let locality: String = place.locality { self.currentAddress.append(locality + " ") }
if let subLocality: String = place.subLocality { self.currentAddress.append(subLocality + " ") }
if let subThoroughfare: String = place.subThoroughfare { self.currentAddress.append(subThoroughfare + " ") }
group.leave()
})
})
좀 더 dispatchGroup에 대해 알아보고자 했다.
<https://beankhan.tistory.com/189>
우리는 왜 dispatchGroup을 사용하는가? <http://seorenn.blogspot.com/2015/08/swift-dispatch-group.html>
연산하는데 시간이 오래 걸리는 경우를 가정해보자. 당연히 기다리는데 지루할 것이다. 그런데 이 연산 작업을 쪼게어서 병렬로 처리하는게 가능하다면 당연히 (남는 CPU 코어를 이용해) 병렬처리 하는 것이 더 빠른 결과를 얻기 위한 방법이다.
다만, 병렬로 계산하기 때문에 모든 병렬 연산 작업이 끝나야 최종 결과물 산출이 가능해진다. 이 모든 병렬 연산이 끝나는 시점을 어떻게 파악할 것인가?
이번 글은 이런 병렬 작업의 처리가 모두 완료되었을 시점을 파악하는 방법, 다르게 말하자면 GCD의 디스패치 그룹(Dispatch Group)에 관한 내용이다.
group.enter()
DispatchQueue.global(qos: .userInitiated).async(group: group, execute: { [unowned self] in
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemark, error) -> Void in
guard error == nil, let place = placemark?.first else {
group.leave()
fatalError("❌ Error, Convert GEO Location.")
}
if let administrativeArea: String = place.administrativeArea { self.currentAddress.append(administrativeArea + " ") }
if let locality: String = place.locality { self.currentAddress.append(locality + " ") }
if let subLocality: String = place.subLocality { self.currentAddress.append(subLocality + " ") }
if let subThoroughfare: String = place.subThoroughfare { self.currentAddress.append(subThoroughfare + " ") }
group.leave()
})
})
자 이제 여기서 도대체 왜 group을 만들어서 사용하나 했는데, location말고 weather, 미세먼지 정보들도 같은 디스패치 큐에서 진행된다면 한 곳에서 이들의 종료를 관리해주지 않을까?
일단 이건 weather 정보 api 요청 부분.
public func receiveDustData(adminArea: String, group: DispatchGroup) {
guard let link: URL = URL(string: "http://openapi.airkorea.or.kr/openapi/services/rest/ArpltnInforInqireSvc/getCtprvnMesureLIst?serviceKey=\(DUST_OPEN_API_KEY)&numOfRows=10&pageSize=10&pageNo=1&startPage=1&itemCode=PM10&dataGubun=HOUR&searchCondition=MONTH(&_returnType=json") else {
fatalError("❌ Error, Not Vaild URL.")
}
DispatchQueue.global(qos: .userInitiated).async(group: group, execute: { [unowned self] in
group.enter()
Alamofire.request(link).responseJSON(completionHandler: { response in
guard response.result.isSuccess else {
group.leave()
fatalError("❌ Error, Not Receive Data From Korea Meteorological Administration Server.")
}
switch (response.response?.statusCode) {
case .none:
group.leave()
print("❌ Error, Not Receive Data From Korea Meteorological Administration Server.")
case .some(_):
guard let result = response.result.value, let json = result as? NSDictionary else { return }
guard let list = json["list"] as? [[String:Any]] else { return }
if let item: [String:Any] = list.first, let administrativeArea: AdministrativeArea = AdministrativeArea(rawValue: adminArea) {
if let currentDust: String = item[administrativeArea.getEnglishAdminArea()] as? String {
self.result = currentDust
print("- Fine Dust: \(currentDust) ㎍/㎥")
}
}
group.leave()
}
})
})
}
이 아래 부분이 viewController에서 group을 생성하고, notify로 기다리는 부분이다.
private func showWeatherInformation() {
SwiftSpinner.show("Just a moment", animated: true)
let group: DispatchGroup = DispatchGroup()
Weather.weatherInstance.receiveWeatherData(group: group, language: "ko", latitude: self.currentLocation.latitude, longtitude: self.currentLocation.longitude)
group.notify(queue: .main, execute: { [unowned self] in
let weatherINF = Weather.weatherInstance.getWeatherInformation()
// Lable
self.weatherStateLB.text = weatherINF.sky
self.humidityStateLB.text = "💧 \(weatherINF.humidity * 100) %"
self.visualityStateLB.text = "🌈 \(weatherINF.visibility) KM"
// Image View
self.weatherLogoIMG.image = UIImage(named: weatherINF.icon)
})
}
private func showDustInformation() {
guard let adminArea = CLocation.locationInstance.getCurrentAddress().split(separator: " ").first else {
fatalError("❌ Error, could not get current address.")
}
let group: DispatchGroup = DispatchGroup()
Dust.dustInstance.receiveDustData(adminArea: String(adminArea), group: group)
group.notify(queue: .main, execute: { [unowned self] in
let dustINF = Dust.dustInstance.getDustReuslt()
self.dustStateLB.text = "💨 \(dustINF) ㎍/㎥"
})
}
notify가 뭐냐 하면 <https://developer.apple.com/documentation/dispatch/dispatchgroup/2016084-notify>
Schedules the submission of a block to a queue when all tasks in the current group have finished executing.
음 번역을 못하겠따 ㅋㅋ
현재 그룹의 모든 작업이 종료되었을때, 한 큐에 제출된 블록을 스케쥴한다. 음 대충 이런뜻인듯. 그룹에 엔터로 시작한 모든 타스크들이 리브로 모두 끝나면 블록을 실행한다는듯!!.
근데 읽고 보니 굳이 여러 타스크가 아니더라도 한개가 끝나도 실행되도록 할 수 있는거네 그럼 좋구나~
group.notify(queue: .main, execute: { [unowned self] in
let weatherINF = Weather.weatherInstance.getWeatherInformation()
// Lable
self.weatherStateLB.text = weatherINF.sky
self.humidityStateLB.text = "💧 \(weatherINF.humidity * 100) %"
self.visualityStateLB.text = "🌈 \(weatherINF.visibility) KM"
// Image View
self.weatherLogoIMG.image = UIImage(named: weatherINF.icon)
})
추가로 사용법을 좀 더 공부해 봐야 할거 같다. 사용하면서 모르겠는 부분이 많아서.
[https://developer.apple.com/documentation/dispatch/dispatchqos/1780708-userinteractive]
userInteractive
The quality-of-service class for user-interactive tasks, such as animations, event handling, or updating your app's user interface.
Getting the Predefined QoS Objects
static let userInitiated: DispatchQoS
The quality-of-service class for tasks that prevent the user from actively using your app.
static let `default`: DispatchQoS
The default quality-of-service class.
static let utility: DispatchQoS
The quality-of-service class for tasks that the user does not track actively.
static let background: DispatchQoS
The quality-of-service class for maintenance or cleanup tasks that you create.
static let unspecified: DispatchQoS
The absence of a quality-of-service class.
'ios개발 > 개념 정리' 카테고리의 다른 글
<스위프트> 뷰에서의 콘텐트 모드, 뷰에 이미지 채우는 방법에 대한 것이다. (0) | 2020.03.07 |
---|---|
<스위프트> CLGeocoder(), CLLocation(), MapView (2) | 2020.03.05 |
<스위프트> typealias (0) | 2020.03.05 |
<스위프트> try catch (0) | 2020.03.05 |
<스위프트> class vs struct (0) | 2020.03.05 |