NSCache와 Purgeable Memory

캐시란

캐시는 애플리케이션의 성능을 크게 향상시킬 수 있는 객체 또는 데이터 모음입니다.

 

캐시를 사용함으로써 계산 비용이 많이 들 수 있는 일시적인 데이터와 함께 자주 접근하는 객체를 저장할 수 있습니다.

캐싱된 객체를 다시 사용하면 해당 값을 다시 계산할 필요가 없기 때문에 성능상의 이점을 얻을 수 있습니다.

 

그러나 특정 상황에서 몇가지 가능한 단점 또한 존재합니다.

많은 대용량 데이터를 캐싱할 때 다른 애플리케이션을 위한 RAM이 남아 있지 않을 정도 많은 객체를 캐시할 수 있으며,

RAM을 확보하기 위해 애플리케이션을 종료할 수 있습니다.

 

iOS에서는 캐싱을 위한 NSCache 객체를 제공하며 생성 비용이 많은 객체를 임시로 저장할 수 있습니다.

이번 포스팅에서는 NSCache와 Purgeable 메모리를 사용하는 방법에 대해 학습해보았습니다.

NSCache

저장 컨테이너로 리소스가 부족할 때 제거될 수 있는 Key-Value 쌍을 저장하는 mutable collection 입니다.

class NSCache<KeyType, ObjectType> : NSObject where KeyType : AnyObject, ObjectType : AnyObject

 

캐시 객체에는 다양한 자동 제거 정책이 있으며 이들을 통합하여 일부 항목을 제거합니다.

자동 제거 정책에는 캐시 객체의 최대 개수를 제한하고 모든 캐시 객체의 최대 cost를 제한하는 두 가지 프로퍼티가 있습니다.

 

countLimit: 캐시에 허용되는 최대 객체 개수를 제한할 수 있습니다.

- 캐시 개수가 이 값을 초과하면 제거될 수 있습니다. 기본 값은 0으로 개수 제한이 없습니다.

 

totalCostLimit: 캐시가 보유할 수 있는 최대 비용을 설정할 수 있습니다.

- 캐시가 최대 cost를 초과하면 제거될 수 있습니다. 기본 값은 0으로 비용 제한이 없습니다.

 

캐시에 객체를 추가할 때, 각 Key-Value 쌍과 연결할 비용 값을 지정하는 메서드는 다음과 같습니다.

 

setObject(_:forKey:): 캐시에 지정된 Key-Value를 설정합니다.

 

setObject(_:forKey:cost): 캐시에 지정된 Key-Value와 cost 값을 설정합니다.

- cost를 전달하지 않으면 0을 전달하거나 cost가 필요하지 않은 setObject(_:forKey:) 메서드를 사용하여야 합니다.

 

NSCache의 특징

- 캐시를 직접 lock 하지 않아도 다른 쓰레드에서 캐시의 항목을 추가, 제거 및 쿼리할 수 있습니다.

- NSMutableDictionary 객체와 달리 캐시는 그 안에 들어 있는 키 객체를 복사하지 않습니다.

- Linked List를 사용하여 데이터 추가 및 삭제에 효율적으로 동작합니다. 탐색 시간 복잡도: O(n)

- 별도의 Dictionary를 두어 데이터 접근에도 용이합니다. 데이터 접근 시간복잡도: O(1)

 

NSCache는 자동으로 객체의 캐싱 및 제거를 처리하지만 객체를 제거할 시기를 제어할 수는 없습니다.

더 이상 필요하지 않은 객체를 제거하는 경우,

NSPurgeableData 또는 NSDiscardableContent 프로토콜을 채택한 타입을 사용할 수 있습니다.

 

NSDiscardableContent

필요한 순간에 사용하던 메모리를 반납해야 하는 객체를 위한 인터페이스를 정의하는 프로토콜입니다.

protocol NSDiscardableContent

NSDiscardableContent 프로토콜은 내부적으로 객체 라이프 사이클을 다루는 Counter 변수가 있습니다.

Counter 변수는 현재 객체가 다른 객체에서 사용 중인지 여부를 추적하는 용도로 사용됩니다.

 

기본적으로 Counter 변수는 1로 초기화됩니다.

beginContentAccess() 메서드를 호출하면 1씩 증가하게 되고 endContentAccess()를 호출하면 1씩 감소합니다.

사용하지 않는 시점에 Counter가 0이되면 discardContentIfPossible() 메서드를 호출하여 해당 시점에 관련 메모리가 해제됩니다.

 

NSPurgeableData

더 이상 필요하지 않을 때 사용하 메모리를 반납해야하는 mutable data 객체입니다.

class NSPurgeableData : NSMutableData

이 클래스는 NSDiscardableContent 프로토콜을 준수하는 NSMutableData의 서브 클래스입니다.

NSPurgeableData는 NSDiscardableContent 프로토콜의 기본 구현이 되어 있습니다.

NSPurgeableData 객체가 NSCache에 추가되었다면 반납될 수 있는 데이터 객체가 반납되면 자동으로 캐시에서 제거됩니다.

 

let cache = NSCache<NSString, NSPurgeableData>()
let image = UIImage()
let data = NSPurgeableData(data: image.pngData()!)
cache.setObject(data, forKey: "image")

if let cachedData = cache.object(forKey: "image" as NSString) {
    // counter += 1
    if cachedData.beginContentAccess() {
        let image = UIImage(data: cachedData as Data)
        // counter -= 1
        cachedData.endContentAccess()
        // counter == 0
    }
}

 

반납될 수 있는 데이터 객체에 접근해야 한다면, 사용하는 동안 반납되는 것을 막기 위해 beginContentAccess()를 호출해야 합니다.

그리고 객체 사용이 끝났을 경우, 객체의 메모리를 반납해도 된다고 알리는 endContentAccess()를 호출해야 합니다.

 

 

 

참고 자료

hcn1519.github.io/articles/2018-08/nscache

developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/Articles/CachingandPurgeableMemory.html

aroundck.tistory.com/4717

'iOS' 카테고리의 다른 글

[SwiftUI] Carousel View 만들기  (6) 2022.09.08
iOS File System  (0) 2021.04.04
OptionSet을 준수하는 커스텀 타입 작성하기  (0) 2021.04.01
Render loop 정리 (작성중)  (0) 2021.03.28
CustomStringConvertible  (0) 2021.03.27

댓글