캐싱은 재사용될 수 있는 자원을 특정 영역에 저장해놓는 것을 의미한다. 캐싱된 데이터가 있다면 추가적인 자원을 소모하지 않고 캐싱 데이터를 가져다 쓸 수 있기 때문에 자원을 절약할 수 있고 애플리케이션의 처리 속도가 향상된다.
메모리 캐시는 휘발성 메모리(RAM)에 데이터를 저장하여 빠른 접근이 가능하지만, 앱이 종료되면 메모리에서 캐시 데이터도 같이 삭제된다. NSCache를 사용하며, 이는 key:value
로 이루어진 제네릭 클래스다.
import UIKit
// MARK: - ImageCache
class ImageCache {
static let shared = NSCache<NSString, UIImage>()
}
// MARK: - ViewController
class ViewController: UIViewController {
let imageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
let imageURL = URL(string: "https://example.com/image.jpg")!
loadImage(from: imageURL)
}
func loadImage(from url: URL) {
let cacheKey = NSString(string: url.absoluteString)
// 메모리 캐시에서 이미지 로드
if let cachedImage = ImageCache.shared.object(forKey: cacheKey) {
imageView.image = cachedImage
return
}
// 메모리 캐시에 이미지가 없으면 네트워크에서 로드
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url), let image = UIImage(data: data) {
DispatchQueue.main.async {
self.imageView.image = image
ImageCache.shared.setObject(image, forKey: cacheKey)
}
}
}
}
}
UIImageView를 extension하는 방법으로도 사용 가능하다.
import UIKit
// MARK: - UIImageView
extension UIImageView {
func setImageUrl(_ urlString: String) {
DispatchQueue.global(qos: .background).async {
let cacheKey = NSString(string: urlString) // 캐시 키 생성
// 메모리 캐시에서 이미지 로드
if let cachedImage = ImageCache.shared.object(forKey: cacheKey) {
DispatchQueue.main.async {
self.image = cachedImage
}
return
}
// 유효한 URL인지 확인
guard let url = URL(string: urlString) else { return }
// URLSession을 사용하여 이미지 다운로드
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
DispatchQueue.main.async {
self.image = nil
}
return
}
DispatchQueue.main.async {
if let data = data, let image = UIImage(data: data) {
// 다운로드한 이미지를 캐시에 저장
ImageCache.shared.setObject(image, forKey: cacheKey)
self.image = image
}
}
}.resume()
}
}
}
디스크 캐시는 FileManager를 사용하여 데이터를 파일 시스템에 저장하여 영구적으로 보존 가능하다.
import UIKit
// MARK: - DiskCache
class DiskCache {
static let shared = DiskCache()
private init() {}
let fileManager = FileManager.default
func cacheDirectory() -> URL {
return fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first!
}
func saveData(_ data: Data, forKey key: String) {
let fileURL = cacheDirectory().appendingPathComponent(key)
do {
try data.write(to: fileURL)
} catch {
print("Error saving data to disk: \(error)")
}
}
func loadData(forKey key: String) -> Data? {
let fileURL = cacheDirectory().appendingPathComponent(key)
return try? Data(contentsOf: fileURL)
}
}
// MARK: - ViewController
class ViewController: UIViewController {
let imageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
let imageURL = URL(string: "https://example.com/image.jpg")!
loadImage(from: imageURL)
}
func loadImage(from url: URL) {
let cacheKey = url.lastPathComponent
// 디스크 캐시에서 이미지 로드
if let cachedData = DiskCache.shared.loadData(forKey: cacheKey), let image = UIImage(data: cachedData) {
imageView.image = image
return
}
// 디스크 캐시에 이미지가 없으면 네트워크에서 로드
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url), let image = UIImage(data: data) {
DispatchQueue.main.async {
self.imageView.image = image
DiskCache.shared.saveData(data, forKey: cacheKey)
}
}
}
}
}