이모저모/Swift
ios - 이미지 중복 사용 방지 처리
ARpple
2024. 1. 25. 17:09
RCManager
앱 내 파일 관리하기
Realm 패키지 의존 모듈
RCManager 구성
- RCManager (Reference Count Manager)는 참조 계수 매니저로 Swift와 Objective-C의 메모리 관리 기법에서 비롯함.
- 샌드박스 내부(로컬)에서 저장해야하는 파일 (이미지, docs 등등...)의 여러 로컬 DB에서 사용하는 경우 파일을 중복 생성해서 보관하는 것을 방지하기 위함
- 각각의 디비 레코드에서 특정 파일의 이름들 보관하는 경우 Reference Count 1 증가
- 특정 파일의 이름을 삭제하는 경우 Reference Count 1 감소...
- Reference Count가 0인 경우 해당 파일 삭제 및 참조 계수 매니저 테이블에 해당 레코드 삭제
RCManager 생성 지침
- 싱클톤 패턴 적용, 하나의 파일 타입을 하나의 매니저 인스턴스로 관리
- Snapshot 패턴 적용, 하나의 파일 타입(이미지, 영상, 문서 등등...)에 맞는 매니저 인스턴스에 apply를 통한 관리
- 파일 저장은 각각의 파일 타입에 따라 다르기 때문에 직접 구현해야함
- 해당 파일 이름이 존재하는지 확인하는 기능 제공
- 파일 ReferenceCount가 0이면 삭제하는 기능 제공
💡 이 서비스 관련 타입은 앞에 RCM이라고 붙임
코어 로직

💡 코어 로직을 상속 혹은 채택해서 실제 파일 타입에 맞는 매니저를 구현한다.
#클래스 - Realm과 직접 연결됨
1. RCMTable - Class
실제 Realm에 담길 래퍼런스 카운트 Realm 테이블 구조
2. RCMRepository - Class
Realm DB 시스템에서 CRUD를 도와주는 Realm Repository... RCMTable과 직접 소통할 수 있다.
3. RCMTableConvertable - Protocol
- 커스텀 Table을 Realm Table을 [RCMTable]로 변환하기 위한 프로토콜
- [RCMRepository]에서 이 프로토콜을 준수한 인스턴스를 사용함
#매니저 관련
1. RCMAble - Protocol ⭐
- 래퍼런스 카운트 매니저 클래스는 프로토콜을 준수해서 구현
2. RCMAble - Struct
- RCMTableConvertable에서 사용하는 제네릭 스냅샷
#FileManager Extension
ReferenceCount에 의해 관리되는 로컬 파일 경로(이름)을 조회하거나 삭제할 수 있는 익스텐션
사용 예시
💡 채팅방 서비스에서 각 사용자별 프로필 이미지를 중복 없이 확인하고 추가하기
var ircSnapShot: ImageRC.SnapShot!
// 특정 서비스[ex: A 채팅방]에서 추가할 이미지 아이디들
func searchSelectionUpdate(ids:[String]){
// 웹 URL 기반으로 로컬 이미지 이름 만들기
let newItems = ids.enumerated().map { ($1.getLocalPathName(type: .search),$0) }
let dict = newItems.reduce(into: [:]) { $0[$1.0] = $1.1 }
let newFileNames = newItems.map{$0.0}
// 기존에 존재한 이미지들을 제외하고 새로 로컬에 저장할 이미지들 (다른 서비스[ex: B 채팅방]에서는 사용하고 있을 가능성이 있다.)
let appendFileNames = Array(Set(newFileNames).subtracting(selectedItems))
// 새로 로컬에 저장할 이미지들의 원래 존재한 인터넷 URL 가져오기
let downloadURLs = appendFileNames.compactMap { dict[$0] }.map{ids[$0]}
Task{
// 만약 로컬이 이미지 이름이 없으면 이미지 다운로드, 있으면 로컬에서 가져온다.
try await ImageService.shared.saveToDocumentBy(searchURLs: downloadURLs,fileNames: appendFileNames)
passthoroughLoading.send(false)
// 특정 서비스[ex: A 채팅방]에 기존에 사용하는 로컬 이미지들과 새로 다운받은 로컬 이미지들을 합치기
self.selectedItems = selectedItems.union(appendFileNames)
// 특정 서비스[ex: A 채팅방]에 새로 추가한 이미지들 ImageRCM 업데이트
await updateImageRC(appends:appendFileNames,deletes:[])
}
}
func updateImageRC(appends: any Sequence<String>,deletes:any Sequence<String>) async {
await appends.asyncForEach {
if !ircSnapShot.existItem(id: $0){
await repository.insert(item: ImageItem(name: $0, count: 0))
}
await ircSnapShot?.plusCount(id: $0)
}
Task.detached {[weak self ] in
await deletes.asyncForEach { await self?.ircSnapShot?.minusCount(id: $0) }
}
}
RCM 코드
RCM(ReferenceCountManager).zip
0.01MB