wifi: 파란색
/ WWAN: 하늘색
/ 네트워크 유실: 빨간색
문제 코드
// TableViewCell.swift
private func setItemImage(imageURL: String) {
// 1. ImageSetter의 download()호출
ImageSetter.download(with: imageURL, handler: { imageData in
// 3. download()함수가 종료되면서 핸들러가실행됨. 같은 main queue라서 문제발생
DispatchQueue.main.sync { [weak self] in
guard let data = imageData else { return }
self?.itemImage.image = UIImage(data: data)
}
})
}
// TableViewCell에서 ImageSetter의 download가 호출
class func download(with url: String, handler: @escaping((Data) -> Void)) {
let cacheURL = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
let imageSavingPath = cacheURL.appendingPathComponent(URL(string: url)!.lastPathComponent)
// 2. 아래 줄 코드의 existFile이 체크되고 handler가 실행됨
if let imageData = existFile(at: imageSavingPath) {
handler(imageData)
} else {
URLSession.shared.downloadTask(with: URL(string: url)!) { (tmpLocation, response, error) in
// do something...
}.resume()
}
}
A블럭
이라고 칭한다.A블럭
의 관련 worker는 다음과 같다.Assignor : setItemImage()
, Assignee : download()
A블럭
을 실행한다.A블럭
의 worker는Assignor : download()
, Assignee : setItemImage()
tableView.reloadData()
)하는 것이아니라 데이터가 변경된 row들만 변경되도록(tableView.insertRows()
) 처리하려했지만 UI업데이트 도중 데이터 변경(비동기적으로 다운로드 진행중)으로 인해 TableView에서 크래시 발생하는 문제.
private func resetTableView(indexPaths: [IndexPath]) {
DispatchQueue.main.sync { [weak self] in // async는 크래시발생
self?.tableView.beginUpdates()
self?.tableView.insertRows(at: indexPaths, with: .automatic)
self?.tableView.endUpdates()
}
}
beginUpdates()
와 endUpdates()
사이의 시점에서는 모델이 변경되면 안됨async
로 동작하도록 구현하면 크래시 발생insertSection/insertRows
, 혹은 reloadSection/rows
로 인해서 변경된 테이블뷰의 데이터 수가 변경 전과 다르다는 에러.
Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'Invalid update: invalid number of sections.
The number of sections contained in the table view after the update (0) must be equal to the number of sections contained in the table view before the update (2), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).
beginUpdates()
와 endUpdates()
사이에서 모델과 테이블뷰가 가진 데이터의 수가 같아야하는게 포인트! (업데이트 전의 데이터가 0개이면 테이블뷰의 rows도 0, 데이터가 변경되서 3개가 되면 rows도 3개여야하며, 에러메시지에서도 언급하고 있는 내용이다.)func downloadTask(with url: URL, completionHandler: @escaping (URL?, URLResponse?, Error?) -> Void) -> URLSessionDownloadTask
/Users/jeonmijin/Library/Developer/CoreSimulator/Devices/DE1DE4FA-2208-4062-8C55-0673E3019F6C/data/Containers/Data/Application/34D0E5CC-B577-49E2-913A-82DDBA91CB59/tmp/CFNetworkDownload_nEtNzv.tmp
이런식으로 임시 파일이 저장됨// 임시 파일 location으로 바로 이미지 가져와봄
func test() {
let url = URL(string: "https://cdn.bmf.kr/_data/product/HCCFE/757878b14ee5a8d5af905c154fc38f01.jpg")!
URLSession.shared.downloadTask(with: url) { (location, response, error) in
if let error = error {
print("\(error)")
}
if let location = location { // 파일이 저장된 url애 접근하여 UIImage생성
let img = UIImage(contentsOfFile: location.path)
DispatchQueue.main.sync {
self.view.addSubview(UIImageView(image: img))
}
}
}.resume()
}
do {
try fileManager.moveItem(at: tmpLocation, to: imageSavingPath)
let imageData = try? Data(contentsOf: imageSavingPath)
handler(imageData)
} catch {
if FileManager().fileExists(atPath: imageSavingPath.path) {
let imageData = try? Data(contentsOf: imageSavingPath)
handler(imageData)
} else { handler(nil) }