티스토리 뷰

iOS/iOS 지식

[iOS] Data(ContentsOf)와 NSCache

홍복치 2024. 6. 22. 22:26

Data(contentsOf:) 란?

 

특정 URL로 부터 Data를 만드는 생성자임

근데 그 아래 안내문이

이 방법은 동기식으로 실행되며 호출 스레드가 끝날 때까지 차단되므로 main 스레드에서 호출하지 마십시오.
대신 file coordination 또는 nonblocking file-related APIs 중 하나를 사용하십시오. 
자세한 내용은 파일 시스템에 액세스할 때 성능 및 안정성 향상을 참조하십시오.

일단 동기적으로 실행된다는 데서 해당 작업이 끝날 때까지 스레드는 일을 하지 못한다는 것을 알 수 있음

작업이 끝날때까지 아무 것도 할 수 없기 때문에 계속해서 사용자의 이벤트를 업데이트 해주어야 하는 메인스레드에서는 호출하면 안됨

 

여기까지 이해하고 그럼 단순하게 DispatchQueue.global().async를 사용하면 되지 않을까? 란 생각을 하게 됨

메인스레드가 아닌 다른 스레드에서 하면 되는게 아닐까

let url = URL(string: "URLURL")!

DispatchQueue.global().async {
    do{
         let data = try Data(contentsOf: url)
                
          DispatchQueue.main.async {
             self.oneImageView.image = UIImage(data: data)
          }
          
       }catch{
            self.oneImageView.image = UIImage(systemName: "heart")
       }
}

메인스레드가 아니라고 블락되도 될까?

다른 모든 스레드를 사용중이라고 했을 때 하나의 스레드가 블락되면 그 이후 작업은?..할 수가 없어짐

 

그러면 일차적 방법은 동기로 동작하지 않고 비동기적으로 동작하는 걸 써보는 것임

애플이 네트워크 관련된 작업을 하기 위해 만들어놓은 URLSession은 비동기적으로 동작해서 스레드를 블락시키지 않고 동작함.

이 URLSession을 사용하면 completionHandler로 통신 시 에러에 대한 처리가 가능해짐

위의 do-try-catch에서는 어떤 에러인지는 알 수 없음

URLSession.shared.dataTask(with: url) { data, response, error in
      guard let data else { return }
      DispatchQueue.main.async {
          self.oneImageView.image = UIImage(data: data)
      }
}.resume()

근데 data(contentsOf)를 사용하면 안되나..?라는 생각이 듬.

왜냐하면 스레드를 블락하지 않을 정도의 데이터이면 상관없지않을까 라는 생각이 들었음

효율적으로 Data(ContentsOf)를 쓰는 방법에 대해 알아보도록 함. 그것은 캐싱

 

이미지를 서버에서 가져오는 유명한 라이브러리인 kingfisher도 캐싱을 한다고 한다.

그럼 캐싱은 뭐하는 거길래 쓰는걸까. 결론부터 말하면 매번 네트워크에서 호출해서 가져다 쓰지말라는 것임..

캐시란?

데이터나 값을 미리 복사해 놓는 임시 장소

캐시 접근시간에 비해 원래 데이터에 접근하는 시간이 더 오래 걸리는 경우나 값을 다시 계산하는 시간을 절약하고 싶은 경우에 사용

캐시에 데이터를 미리 복사해놓으면 계산이나 접근 시간 없이 더 빠른 속도로 데이터 접근이 가능함

iOS 캐시 종류

메모리 캐시

앱의 메모리 영역의 일부분을 caching에 사용

앱이 종료되어 메모리에서 해제되면 저장 데이터 날아감(휘발성)

저장공간은 적지만, 처리 속도가 빠름

 

디스크 캐시

FileManager를 사용해 데이터를 파일 형태로 디스크에 저장

UserDefaults, CoreData 등도 디스크 캐시에 포함

앱이 종료되어도 데이터가 남음(비휘발성(영속성)) > 앱이 차지하는 용량 자체가 커짐

저장공간이 크고, 처리속도는 느림(네트워크를 통해서 다운로드하는 것보단 빠름)

Kingfisher는 어떤 방식으로 캐싱을 할까

급 궁금해지는 kingfisher의 캐싱 방식... 여기서 중요한 건 아니지만 궁금하니깐 간단하게만 찾아봄

Multiple-layer hybrid cache for both memory and disk.

결론은 둘 다 쓴다 임..

메모리는 속도적 측면에서 좋지만 앱이 종료되면 날라가고 디스크는 앱이 종료되도 저장되니까 이를 함께 사용해서 보완한다고 함.

처음 이미지 다운로드 -> 디스크랑 메모리에 둘 다 캐싱 -> 가져올땐 속도 빠른 메모리 -> 없으면 디스크 -> 없으면 URL으로 이미지 다운 -> 앱 재시작 이후 요청에서는 디스크의 이미지를 다시 메모리에 추가

이런 식으로 동작한다고 함. 

NSCache?

 

임시 키-쌍을 저장하는데 사용하는데 사용하는 변경 가능한 컬렉션임

뭔지 잘 모르겠지만 키와 값이 함께 저장된다는 것 같음. 약간 UserDefaults랑 느낌이 비슷한 것 같기도 함.

 

NSCache 클래스에는 캐시가 시스템 메모리를 너무 많이 사용하지 않도록 보장하는 다양한 자동 제거 정책이 있음

-> 메모리 부족 시 자동 폐기 될 수 있고, 필요할 때 다시 값을 계산해야 함

-> 캐시에서 객체를 검색할 때 존재하는지 nil 체크가 필요함

캐시는 저장된 주요 객체를 복사하지 않음

컨텐츠가 삭제되면 디폴트로 캐시 객체도 제거(자동 제거 정책은 변경될 수 있음)

NSCache로 이미지 캐싱해보기

final class NSCacheManager {
    private init(){ }
    static let shared =  NSCache<NSString, UIImage>()
}
func getImage(){
   let imageName = "https://apod.nasa.gov/apod/image/2406/AraDragons_Taylor_4728.jpg"
   let cachedKey = NSString(string: imageName)
   let url = URL(string: imageName as String)!
            
   if let cacheImage = NSCacheManager.shared.object(forKey: cachedKey){
       self.oneImageView.image = cacheImage
       return
     }
            
    DispatchQueue.global().async {
        let data = try? Data(contentsOf: url)
        let image = UIImage(data: data!)
        NSCacheManager.shared.setObject(image!, forKey: cachedKey)
                
       DispatchQueue.main.async {
           self.oneImageView.image = image
        }
      }     
  }

앱을 실행해보면 두 번째 실행 때는 확연히 속도가 떨어진 것을 볼 수 있음

하지만 앱을 종료하고 다시 켜면 다시 받아와야해서 시간이 오래 걸림

Cache Memory : 4.358345985412598 //1번째 실행
Cache Memory : 0.00020492076873779297 //2번째 실행
Cache Memory : 5.4634599685668945  //앱 종료 후 1번째 실행

 

참고자료

https://developer.apple.com/documentation/foundation/nscache/

https://dev-dain.tistory.com/289

https://jryoun1.github.io/swift/Cache/

https://velog.io/@mmim/iOS-swift-Cache%EC%99%80-NSCache-%EA%B0%9C%EB%85%90

https://velog.io/@hii5074/Image-Caching

https://poky-develop.tistory.com/35

https://velog.io/@ryalya/iOS-CS-Study-Cache-Memory%EB%9E%80https://kirkim.github.io/ios/2022/08/08/nscache.html

 

최근에 올라온 글
Total
Today
Yesterday