iOS

NSCache로 Image Cache를 구현해보자!

TaeJoongYoon 2019. 5. 19. 22:25

보통 URL를 통한 Image Download 작업을 할 때, Kingfisher 라이브러리를 사용한다.

이 때, Kingfisher는 Cache도 지원해주는데 이 Cache를 NSCache로 직접 만들어보자!

NSCache로 Image Cache를 구현해보자!

 

먼저 iOS에는 NSCache라는 것을 지원해준다.

Apple 공식 Document에서도 찾아볼 수 있는데 아래와 같다.

 

 

설명을 읽어보면 key-value로 Cache를 지원하고 Generic Class이기 때문에 각 Type을 지정해줘야 하는 것 같다.

먼저, Cache없이 url로 이미지를 받아오는 작업을 해보자!

 

 

이미지는 아이언맨과 캡틴아메리카로 해봤습니다...ㅎㅎ

"Image Download" 버튼을 누르면 URLSession을 이용하여 data를 받아오고 각 ImageView에 넣는 작업을 했습니다.

@IBAction func download(_ sender: UIButton) {
    let ironmanURL = URL(string: ironmanURLstring)
    URLSession.shared.dataTask(with: ironmanURL!) { data, response, error in
      guard let data = data else { return }
      let image = UIImage(data: data)!
      DispatchQueue.main.async {
        self.ironmanImageView.image = image
      }
      }.resume()
    
    let captainAmericaURL = URL(string: captainAmericaURLstring)
    URLSession.shared.dataTask(with: captainAmericaURL!) { data, response, error in
      guard let data = data else { return }
      let image = UIImage(data: data)!
      DispatchQueue.main.async {
        self.captainAmericaImageView.image = image
      }
      }.resume()
    
  }

 

각 이미지의 데이터 크기 차이가 있어서 아이언맨이 먼저 불러오고 캡틴아메리카 이미지가 불러와집니다.

그리고 Clear후, 다시 이미지를 불러오면 첫 시도와 비슷한 시간이 지나 이미지가 로드됩니다.

그럼 여기서 아이언맨이미지를 Caching해보겠습니다.

 

먼저, NSCache를 만들어줍니다.

let ironmanCache = NSCache<NSString, UIImage>()

 

NSCache의 KeyType과 ObjectType은 모두 AnyObject로 제약이 걸려있습니다.

그래서 Key는 NSString (String은 Struct이기 때문에 Class 타입인 NSString로 설정), Object는 UIImage로 했습니다.

그리고 Data를 받아온 부분에서 이 Cache에 object를 set해줍니다.

self.ironmanCache.setObject(image, forKey: self.ironmanCacheKey as NSString)

 

이 과정을 통해 ironmanCache에 아이언맨 이미지를 캐싱작업했습니다!

이제 불러오는 과정만 남았습니다.

불러오는 과정은 아래와 같습니다.

 

if let ironmanCachedImage = ironmanCache.object(forKey: ironmanCacheKey as NSString) {
	self.ironmanImageView.image = ironmanCachedImage
}

 

NSCache의 object() 메소드를 이용하여 object를 가져올 수 있습니다.

하지만 이 메소드의 리턴값은 Optional이기 때문에 저는 옵셔널 바인딩으로 캐싱을 진행했습니다.

이 과정을 하나로 합치면 아래와 같습니다.

if let ironmanCachedImage = ironmanCache.object(forKey: ironmanCacheKey as NSString) {
      self.ironmanImageView.image = ironmanCachedImage
    } else {
      let ironmanURL = URL(string: ironmanURLstring)
      
      URLSession.shared.dataTask(with: ironmanURL!) { data, response, error in
        guard let data = data else { return }
        print("IRONMAN : ", data)
        let image = UIImage(data: data)!
        self.ironmanCache.setObject(image, forKey: self.ironmanCacheKey as NSString)
        DispatchQueue.main.async {
          self.ironmanImageView.image = image
        }
        }.resume()
    }

 

기존 url을 통해 이미지를 받아오는 과정에서 Cache가 존재하는지를 체크하고 없다면 이미지를 불러와 Caching하는 과정을 추가했습니다.

 

위 Cache 작업을 추가한 화면은 아래와 같습니다.

 

 

 

처음 이미지 로드과정은 Cache가 존재하지 않기 때문에 로드하는 속도가 비슷합니다.

하지만 이후 로드과정은 Cache가 존재하기 때문에 아이언맨 이미지를 로드하는 속도가 훨씬 빠른게 눈에 보입니다!!

 

 

Kingfisher는 Cache를 memory 혹은 disk에 저장하지만 위와같은 NSCache는 memory에 저장하는 방식입니다.

(disk에 저장하기 위해서는 FileManager를 사용해야 하는 것 같습니다..!)

그래서 만약 다른 작업을 위해 memory가 필요한 상황이 생긴다면 System은 삭제할 수도 있다고 합니다 😅

 

 

Reference


Kingfisher GitHub
NSCache Document