매일 공부 일기

(2022-11-11) 소소한 개발 공부

HJ39 2022. 11. 11. 23:18

-오늘 한일

 

# 컴퓨터 응용 설계 팀프로젝트

 

1. 어제 못한 캐시 이용한 이미지 불러오기 공부

 

캐시를 하는 방법으로 두가지가 존재한다.

  • 메모리 캐시
  • 디스크 캐시
  메모리 캐시 디스크 캐시
특징 - 앱의 메모리 일부분을 캐싱하는 곳에 사용한다.
- 앱이 종료되면 사용하던 메모리를 OS에 반납하여 메모리     캐시가 되어 있던것들이 반납된다.
- 데이터 형태로 파일에 저장하는 방식
- 디스크 캐시를 이용하면 이용할 수 록 앱의 용량이 커지게 된다.
- 앱을 종료해도 캐시되었던 것이 삭제되지 않는다.

이미지를 불러올 때 당연하겠지만 불러오는 속도는 메모리캐시 > 디스크 캐시 > 네트워크 통신 순으로 메모리 캐시가 가장 빠르다.


애플에서는 메모리 캐시를 지원하는 NSCache 클래스를 지원해 준다. 👍

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

 

NSCache 특징
- NSCache를 이용하면 메모리 캐시를 사용할 수 있고 thread-safeness를 지원해서 여러 쓰레드들에서 접근해도 개발자가 직접적으로 lock       할 필요가 없다.
- NSCache는 NS Dictionary와 다르게 key객체를 복사하지 않는다고 합니다..? -> 공부를 더 해야할 것 같다.

 

- 참조한 블로그에서는 NSDictionary의 경우 아래 사진과 같이 NSCopying을 따른다고 한다.

참조한 1번 블로그 글

NSDictionary는 Key가 수정되었을 때 그에 해당하는 value를 찾기 위해 NSCopying을 했다면

NSCache의 경우에는 Cache된 데이터가 있을 때 서버에서 key값을 변경한 경우 해당 key값이 클라이언트에서도 변경이 되어야 하는데 변경이 되지 않으면 오류가 발생해서 NSCopying을 하지 않는 것 같다 (내 생각)

 

을 하자마자 블로그 글 아래부분에 써있었다...;;

NSCopying을 쓰지 않는 이유

 


 

팀 프로젝트에 적용 !

 

a.  메모리 캐시 적용

  •  캐싱하는 부분 
//메모리 캐시를 이용하기 위한 캐시key값 설정 여기서는 이미지 url을 사용하였다.
let cacheKey = NSString(string: "\(url)")   
        
//메모리에 캐시된 이미지가 있는 경우
if let cacheImage = ImageCacheManager.shared.object(forKey: cacheKey){
    img.image = cacheImage
    return
}

 

  • ImageCacheManger class
import UIKit

class ImageCacheManager{
    static let shared = NSCache<NSString , UIImage>()
    private init() {}
}

 

 

 

 

이미지 파일 로딩되는 시간이 매우 단축되었다!! 😀😀😀

메모리 캐시가 잘되고 있나보다 ㅎㅎㅎ


b. 디스크 캐시 적용

 

디스크 캐시 특징
- 휴대폰의 디스크에 파일로 이미지를 저장해 두었다가 불러오는 기능이다.
- 메모리 캐시보다 속도가 상대적으로 느리기 때문에 메모리 캐시를 먼저 시도하고 이미지가 없는 경우 디스크 캐시를 시도한다.
- FileManager를 사용하여 파일을 관리한다. (이름이 직관적이라 좋다 ㅎㅎ)
   Filemanager.default를 기본으로 사용한다고 한다.

 

디스크 캐시를 사용하기 위해서 다음과 같은 변수가 필요하다.

guard let path = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first else {
    return
}	//파일의 경로를 표시하는 변수
var filePath = URL(fileURLWithPath: path)	
filePath.appendPathComponent(url.lastPathComponent)

-  파일을 저장할 경로를 가져온다

- lastPathComponent를 이용하여 실제 이미지의 이름 (예시 dog.png) 만 사용한다.

 

  • 파일이 있는지 확인하는 구문
if FileManager.default.fileExists(atPath: filePath.path) {
    guard let imageData = try? Data(contentsOf: filePath) else {
        return
    }
    guard let image = UIImage(data: imageData) else {
    	return
    }
img.image = image

- 파일이 있는지 확인한다.

- imageData에 filepath를 이용해서 Data를 가지고 온다. 이때 Data는 실제 data형태

- 가져온 Data를 UIImage타입으로 변경시킨다.

- 그 후 이미지를 띄운다.

 

  • 파일이 없는 경우 파일 생성하는 구문
if !FileManager.default.fileExists(atPath: filePath.path) {
    FileManager.default.createFile(atPath: filePath.path, contents: resizeImg.pngData(),
   	attributes: nil)

	img.image = resizeImg
}

- 파일이 없는 경우 파일을 생성하기 위해 png파일의 형태로 저장해 주었다.

 

  • 실행 화면

- 디스크 캐시를 적용해서 앱 종료 후에 실행해도 이미지를 빠르게 불러온다.


c. 전체 코드

 func load(img:UIImageView, url: URL, screenWidth: CGFloat) {
        
        let cacheKey = NSString(string: "\(url)")   //메모리 캐시를 이용하기 위한 캐시key값 설정 여기서는 이미지 url을 사용하였다.
        
        //디스크 캐시에 필요한 변수
        guard let path = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first else { return }
        var filePath = URL(fileURLWithPath: path)   //파일 경로
        filePath.appendPathComponent(url.lastPathComponent) //이미지 실제 이름을 사용하는 lastPathComponent 사용
        
        //메모리에 캐시된 이미지가 있는 경우
        if let cacheImage = ImageCacheManager.shared.object(forKey: cacheKey){
            img.image = cacheImage
        }
        else if FileManager.default.fileExists(atPath: filePath.path) { //디스크에 이미지가 히트된 경우 실행
            guard let imageData = try? Data(contentsOf: filePath) else { return } //저장된 이미지 Data를 가져옴
            guard let image = UIImage(data: imageData) else { return }  //Data를 UIImage타입으로 변경
            let resizeImg = image.resize(newWidth: screenWidth,newHeight: (screenWidth / 2))
            img.image = resizeImg
        }
        else{ // 메모리 or 디스크에 캐시된 이미지가 없는 경우 네트워크 통신이 이루어진다.
            DispatchQueue.global().async { 
                if let data = try? Data(contentsOf: url) {
                    if let image = UIImage(data: data) {
                        DispatchQueue.main.async {
                            //네트워크 통신을 하고 메모리와 디스크에 저장시킴
                            let resizeImg = image.resize(newWidth: screenWidth,newHeight: (screenWidth / 2)) //이미지 크기 조절
                            ImageCacheManager.shared.setObject(resizeImg, forKey: cacheKey) //메모리 캐시에 사용할 수 있게끔 올림
                            //디스크에 저장하기 위해 파일을 만들어서 디스크에 저장
                            if !FileManager.default.fileExists(atPath: filePath.path) {
                                FileManager.default.createFile(atPath: filePath.path, contents: resizeImg.pngData(), attributes: nil)
                                img.image = resizeImg
                            }
                        }
                    }
                }
            }
        }
        
        
    }

- 최대한 주석으로 설명을 써보았다.

 

 

 

 

# 참조한 블로그

1. https://beenii.tistory.com/187

2. https://nsios.tistory.com/58

3. https://memohg.tistory.com/119