-
Notifications
You must be signed in to change notification settings - Fork 1
Description
- iOS 15 ๋ถํฐ ์ ์ฉ์ด ๊ฐ๋ฅํ
prepareThumbnail(of:completionHandler:)๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ ๋๊ธฐ์ ์ฝ๋์์ background ์ค๋ ๋์์ ๋น๋๊ธฐ์ ์ผ๋ก thumbnail image ๋ฅผ ๋ง๋๋ ๊ฒ์ ํด๋ณด์! - asnyc ๋ก ์ ์ธ๋ ๋น๋๊ธฐ์ ๋ฉ์๋์ธ
byPreparingThumbnail(ofSize:)๋ฅผ ์ฌ์ฉํด๋ณด์! - debug navigator ๋ก CPU, Memory ์ ์ค์ ๋ก ์ ํจํ์ง ํ์ธํด๋ณด์!
Meet async/await in Swift - WWDC21 - Videos - Apple Developer
WWDC 21 ์ธ์ ์ ๋ณด๋ค๊ฐ ๋น๋๊ธฐ์ ์ผ๋ก thumbnail image ๋ฅผ ๋ง๋๋ ๋ฉ์๋๊ฐ ๋ณด์ฌ์ ์ ์ฉํด๋ณด๊ธฐ๋ก ํ์๋ค.
๋จผ์ ๊ฐ๋ฐ์ ๋ฌธ์๋ฅผ ํ์ธํด๋ณด์.
prepareThumbnail(of:completionHandler:)
Creates a thumbnail image at the specified size asynchronously on a background thread.
Discussion
Concurrency Note
completion handler ๋ฅผ ์ฌ์ฉํ์ฌ ๋๊ธฐ ์ฝ๋์์ ์ด ๋ฉ์๋๋ฅผ ํธ์ถํ๊ฑฐ๋ ๋ค์ ์ ์ธ์ด ์๋ ๋น๋๊ธฐ ๋ฉ์๋๋ก ํธ์ถํ ์ ์์ต๋๋ค.
func byPreparingThumbnail(ofSize size: CGSize) async -> UIImage?UIImageView ์ ์ด๋ฏธ์ง๋ฅผ ํ์ํ ๋, view ์ contentMode ํ๋กํผํฐ๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฏธ์ง๋ฅผ ์๋์ผ๋ก ์๋ฅด๊ฑฐ๋ ํฌ๊ธฐ๋ฅผ ์กฐ์ ํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๊ธฐ๋ณธ ์ด๋ฏธ์ง ํฌ๊ธฐ๊ฐ ๋ทฐ์ bounds ๋ณด๋ค ํจ์ฌ ํฐ ๊ฒฝ์ฐ ์ ์ฒด ํฌ๊ธฐ ์ด๋ฏธ์ง๋ฅผ ๋์ฝ๋ฉํ๋ฉด ๋ถํ์ํ ๋ฉ๋ชจ๋ฆฌ ์ค๋ฒํค๋๊ฐ ์์ฑ๋ฉ๋๋ค. ์ด ๋ฐฉ๋ฒ์ ์ฌ์ฉํ์ฌ ์ง์ ๋ ํฌ๊ธฐ๋ก thumbnail ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ฉด ์ ์ฒด ํฌ๊ธฐ๋ก ์ด๋ฏธ์ง๋ฅผ ๋์ฝ๋ฉํ๋ ์ค๋ฒํค๋๋ฅผ ํผํ ์ ์์ต๋๋ค.
์ด ๋ฉ์๋๋ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ thumbnail ์ด๋ฏธ์ง๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ์์ฑํ๊ณ ํด๋น ์ค๋ ๋์์ completion handler ๋ฅผ ํธ์ถํฉ๋๋ค. ์ฑ์ด completion handler ์์ UI ๋ฅผ ์ ๋ฐ์ดํธํ๋ ๊ฒฝ์ฐ์๋ main thread ์์ UI ์ ๋ฐ์ดํธ๋ฅผ ์์ฝํด์ผ ํฉ๋๋ค.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as? ItemCell else {
fatalError("Unexpected type for cell. Check configuration.")
}
let item = items[indexPath.item]
cell.nameLabel?.text = item.name
item.image.prepareThumbnail(of: thumbnailSize) { thumbnail in
DispatchQueue.main.async {
cell.thumbnailImageView?.image = thumbnail
}
}
return cell
}byPreparingThumbnail(ofSize:)
ํด๋น ๋ฉ์๋๋ ๊ฐ๋ฐ์ ๋ฌธ์์ ๋ณ๋๋ก ํ์ด์ง๊ฐ ์กด์ฌํ์ง ์๊ณ , ์์ ๊ฐ๋ฐ์ ๋ฌธ์์์๋ง ํ์ธํ ์ ์์์ต๋๋ค. ๋์ผํ ๊ธฐ๋ฅ์ด์ง๋ง asnyc context ์์ ์ฌ์ฉํ ์ ์๋ ์ ์ด ๋ฌ๋์ต๋๋ค.
async ๋ฉ์๋์ธ byPreparingThumbnail(ofSize:) ๋ฅผ ์ฌ์ฉํด ๋ณด๊ฒ ์ต๋๋ค.
Meet async/await in Swift - WWDC21 - Videos - Apple Developer
์์ ์ธ์ ์ ์ฝ๋๋ฅผ ์ฐธ๊ณ ํ์ฌ ์งํํ๊ฒ ์ต๋๋ค.
import UIKit
extension UIImage {
var thumbnail: UIImage? {
get async {
let size = CGSize(width: 100, height: 140)
return await self.byPreparingThumbnail(ofSize: size)
}
}
}
// ์ฌ์ฉ
guard let thumbnailImage = await cache[url]?.thumbnail else { throw ImageDownloadError.unsupportImage }read-only properties ๋ asnyc ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
import UIKit
actor ImageDownloader {
static let shared = ImageDownloader()
private init() { }
private var cache: [URL: UIImage] = [:]
func image(from urlPath: String) async throws -> UIImage? {
guard let url = URL(string: Const.Path.imageURLPath + urlPath) else {
throw ImageDownloadError.invalidURLString(Const.Path.imageURLPath + urlPath)
}
if let cached = cache[url] {
return cached
}
let image = try await downloadImage(from: url)
cache[url] = cache[url, default: image]
// ๐ฅ ์บ์ฑ๋ ์ด๋ฏธ์ง์ thumbnail ์ ๋ง๋ค์ด์ ๋ฐํ.
guard let thumbnailImage = await cache[url]?.thumbnail else { throw ImageDownloadError.unsupportImage }
return thumbnailImage
}
private func downloadImage(from url: URL) async throws -> UIImage {
let imageFetchProvider = ImageFetchProvider.shared
return try await imageFetchProvider.fetchImage(with: url)
}
}์ ์ฉ ๊ฒฐ๊ณผ
๊ฐ์ ๋์๋์ง ํ์ธํด๋ณด๊ธฐ ์ํด์ debug navigator ๋ฅผ ํ์ธํด๋ณด์๋ค.
์กฐ๊ฑด)
- ์๋๋ก ๋๊น์ง ์คํฌ๋กค ํ ํ, ์๋ก ๋ค์ ํ๋ฒ ์คํฌ๋กคํ์๋ค.
- ์บ์ฑ๋ ์ด๋ฏธ์ง์ thumbnail ์ ์ฌ์ฉํ์ฌ CPU์ Memory ๋ฅผ ์ ์ฝํด๋ณด์.
thumbnail ์ฌ์ฉ ํ)
โฆ? thumbnail ์ ์ฌ์ฉํ ํ CPU, Memory ๋ ๋ค ์ํญ ์ค์์ง๋ง.. ํ๋ฐ๋ถ์ CPU ๋ฅผ ๋ ๋ง์ด ์ฌ์ฉํ๋ ๊ฒ์ ํ์ธ ํ ์ ์์๋ค.(๋ฐ์ผ๋ก ๋ด๋ฆฐ ์คํฌ๋กค์ ์๋ก ์ฌ๋ฆด ๋)
๊ฐ์
func image(from urlPath: String) async throws -> UIImage? {
guard let url = URL(string: Const.Path.imageURLPath + urlPath) else {
throw ImageDownloadError.invalidURLString(Const.Path.imageURLPath + urlPath)
}
if let cached = cache[url] {
return cached
}
let image = try await downloadImage(from: url)
// ๐ฅ thumbnail ์ด๋ฏธ์ง๋ฅผ ์บ์ฑํด์ฃผ๋๋ก ๋ณ๊ฒฝ.
// ๐ฅ ์ด์ ์๋ ์บ์ฑ๋ ์ด๋ฏธ์ง๋ ๊ทธ๋๋ก. ์บ์ฑ๋์ง ์์ ์ด๋ฏธ์ง๋ thumbnail ๋ก ๋ณ๊ฒฝํด์ ๋ฐํํ๋ค.(์ฆ, ์บ์ฑ๋์ง ์์ ๊ฒฝ์ฐ๋ง thumbnail ๋ก ๋ณ๊ฒฝ)
guard let thumbnailImage = await image.thumbnail else { throw ImageDownloadError.unsupportImage }
cache[url] = cache[url, default: thumbnailImage]
return cache[url]!
}๋งค๋ฒ ๊ฒฐ๊ณผ๊ฐ ๋์ผํ์ง๋ ์๊ฒ ์ง๋ง ์ ์ฒด์ ์ผ๋ก ๋ณด์์ ๋ CPU ์ Memory ๊ฐ ํ์คํ ์ค์์ต๋๋ค.
๋๋์
ํ์๋ผ๋ฉด ๊ทธ๋ฅ ์์๋๋ ๊ฒฐ๊ณผ๋ก์จ ๋์ด๊ฐ ์ ์์ ๋ถ๋ถ๋ค์ธ๋ฐ ์ค์ ๋ก ํ์ธ์ ํด๋ณด๋๊น ๊ฐ๋จํ๊ฒ ๋ด๊ฐ ์์ฑํ ์ฝ๋๊ฐ ์ผ๋ง๋ ์ ํจํ์ง ์ ์ ์์๋ค. ๋ํ, ์ด๋ฅผ ํตํด์ ์์ธ์ ์ฐพ๊ณ ์ฝ๋๋ ๊ฐ์ ํ ์ ์์๋ค.
์ฝ๋๋ฅผ ๋ณด๊ณ ๊ฒฐ๊ณผ๋ฅผ ์์ธกํ๊ณ , ์๋ฎฌ๋ ์ดํฐ์์ ๋๋์ ์ผ๋ก ์๊ณ ๋์ด๊ฐ๋ ๊ฒ๋ค์ ์ค์ ๋ก ์์น์ ์ผ๋ก ํ์ธํ ์ ์์ด์ ์ฝ๋๋ก ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ํ๋ ๊ฒ์ด ์๋ ์ค์ง์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ด์ ๋ฟ๋ฏํ๋ค.
์ถ์ฒ:







