테이블뷰에서 NSCache 사용하기

서버 API에서 목록을 받아 테이블 뷰에 이를 표시할 때 이미지같은 어느정도 사이즈가 있는 것을 셀마다 넣어 줄 때가 있습니다. 이미지들을 넣을때는 보통, 서버에서 이를 받아와서 화면에 나타내는 식으로 처리를 합니다. 그런데 테이블 뷰의 셀은 reuse로 인해 화면 바깥으로 나간 후 다시 화면에 보이면 재사용하는 식으로 작동합니다.

UIImage를 extension해서 셀의 이미지를 다운로드할 때 print를 찍어보았습니다. 셀이 reuse 되면서 계속해서 이미지를 다운로드 한다는걸 볼 수 있습니다. 테이블 뷰가 reuse 될때마다 계속 다운로드 하는것은 사용자의 많은 자원 소모와 앱의 성능 저하를 야기합니다. 이러한 문제를 해결하기 위해 NSCache를 이용합니다.

 

NSCache란?

기본적인 개념은 캐시와 비슷합니다.

  • 임시로 데이터를 저장하고 빠르게 꺼내 쓸 수 있습니다. 
  • 캐시가 너무 많은 시스템 메모리를 사용하지 않도록 하는 다양한 자동 제거 정책이 있습니다.
  • 캐시에 저장된 key 오브젝트를 복사하지 않습니다.

애플 공식 문서에서는 만드는데 비용이 많이 드는 일시적인 데이터가 있는 개체를 임시적으로 저장을 하는데 사용 한다고 합니다. 이번시간에는 Image를 저장하고 테이블뷰 셀이 reuse 될 때 다시 다운로드 받는게 아닌 NSCache에서 꺼내서 사용해보겠습니다.

 

NSCache를 다루는 객체 

checkProfileImageInCache 함수는 캐시에 해당 키값이 있는지를 체크 합니다. 해당 키값이 있다면 UIImage를 리턴해줍니다.

final class ProfileImageCacheManager {
    
    static let shared = ProfileImageCacheManager()
    
    let cache = NSCache<NSString, UIImage>()
    
    private init() {}
    
    func checkProfileImageInCache(imageURL: String) -> UIImage? {
        if let profileImage = cache.object(forKey: NSString(string: imageURL)) {
            return profileImage
        }
        return nil
    }
}

UIImage extension

이미지 URL을 key값으로 저장합니다. load 함수를 실행을 할 때 우선, checkProfileImageInCache로 체크합니다. 만약 해당 이미지가 없다면 nil이 반환이 되므로 아래 코드를 실행합니다. setObject 함수를 통해 이미지 URL을 키값으로, 다운받은 이미지를 함께 캐시에 저장합니다. 이렇게 되면 앱을 처음 실행시켰을 때는 이미지를 다운받지만, 한번 다운로드 받은 후에는 캐시에서 해당 이미지가 있는지 체크, 그 후 캐시에서 이미지를 꺼내 사용할것입니다.

extension UIImageView {
    func load(urlString: String) {
        if let image = ProfileImageCacheManager.shared.checkProfileImageInCache(imageURL: urlString) {
            self.image = image
            return
        }
        DispatchQueue.global().async { [weak self] in
            guard let url = URL(string: urlString) else { return }
            if let data = try? Data(contentsOf: url) {
                if let image = UIImage(data: data) {
                    DispatchQueue.main.async {
                        ProfileImageCacheManager.shared.cache
                        .setObject(image, forKey: NSString(string: urlString))
                        self?.image = image
                    }
                }
            }
        }
    }
}

테이블 뷰 셀이 reuse될 때 이미지를 캐시에서 가져오는것을 볼 수 있습니다.