일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- Apple Developer Academy
- IOS
- 오브젝트
- Codable
- 운영체제
- @state
- decode
- 가상 메모리
- Linked List
- 앨런
- 100 days of SwiftUI
- Swift
- 동기화
- 비동기
- 프로세스 스케줄링
- 인프런
- scrollview
- deadlock
- 데드락
- async
- Algorithm
- struct
- 상호배제
- forEach
- COLOR
- UserDefaults
- 알고리즘
- core data
- SwiftUI
- 동시성
Archives
- Today
- Total
기어가더라도 제대로
PHPickerController와 효율적 메모리 사용(feat. memory leak) 본문
사건 개요
PHPickerViewController 를 사용할 때, 엄청난 메모리 압박에 시달렸다.
- 15장 사진 업로드 비교
사용법
엄청 큰 UIImage의 메모리 사용량
UIImage 자체의 크기가 매우 커서 변화를 줘야했다.
- 기존
- result → UIImage → pngData → UIImage
- 변경
- result → URL (→ cgImage → Data) → UIImage
- 괄호 안은 보이지 않고, 내부적으로 동작하는 부분
- result → URL (→ cgImage → Data) → UIImage
결론적으로 UIImage 의 크기가 엄청 커서 이를 작게 설정하는 것이 중요하다.
픽셀(사진 화질)의 규모를 원본보다 작게 설정하는 것이 핵심이다.
UIImage를 이용할 때 들어가는 메모리의 양은 실제 사진 이미지 파일의 크기가 아니다.
실제 이미지 파일을 불러와 r,g,b,alpha 값을 구성해야하기 때문에 다음과 같은 공식이 만들어 진다.
Memory size of a image = (image size * 4) * pixel scale
이미지를 4개 겹친 상태에서 pixel 규모 크기 만큼 곱한다. 그래서 pixel scale 을 줄이는 전략으로 접근하였다.
구현 해보죠
url extension 부분이 복잡하게 느껴진다.
하나하나씩 부셔봅시다
(부셔지는 건 나였고)
- 1. URL로 작은 크기의 CGImage를 만들고,
- 2. CGImage를 Data로, 만든 Data를 UIImage로 만드는 코드입니다.
- 보다 작은 크기의 CGImage를 만드는 코드
maxDimensionsInPixel
변수를 눈여겨 보시면 크기를 픽셀 스케일을 조절하는 부분이라는 것을 알 수 있습니다.
let sourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
// URL -> CgImageSource
guard let source = CGImageSourceCreateWithURL(self as CFURL, sourceOptions) else { return nil }
// Screen size
let scale: CGFloat = UIScreen.main.scale
// Pixel scale fitted to PhotoView
/// 이건 지금 프로젝트 환경에 맞추기 위해 적은 코드
/// PhotoView의 가장 긴 변을 기준으로 scale 과 곱해서 변수에 저장
let maxDimensionsInPixel = max(ShapeSize.height, ShapeSize.width) * scale
// Downsampling options
let downsampleOptions = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: maxDimensionsInPixel,
] as CFDictionary
guard let cgImage = CGImageSourceCreateThumbnailAtIndex(source, 0, downsampleOptions) else { return nil }
- CGImage 를 바탕으로 Data를 만들어 UIImage를 리턴하는 코드
- CGImgae → Data 로 갈 때 어떤 처리가 이뤄집니다.
- cgImage가 png인지 아닌지에 따라 압축률이 달라지는 코드입니다.
- 어떤 게시물에서는
CGImageDestinationCreateWithData
의 두번째 매개변수에서kUTTypeJPEG
라고 사용하는데 이는 deprecated 된 코드입니다. 아래대로 써야 해요.
// Convert cgImage to Data
let data = NSMutableData()
guard let imageDestination = CGImageDestinationCreateWithData(data, UTType.jpeg.identifier as CFString, 1, nil) else { return nil }
let destinationProperties = [kCGImageDestinationLossyCompressionQuality: cgImage.isPNG ? 1.0 : 0.75] as CFDictionary
CGImageDestinationAddImage(imageDestination, cgImage, destinationProperties)
CGImageDestinationFinalize(imageDestination)
return UIImage(data: data as Data)
전체 코드
UIImage 초기화 방식에 따른 메모리 차이
UIImage
를 named 방식으로 생성하는지, imageWithContentsOfFile 방식으로
생성하는지에 따라 메모리 사용량에서 차이가 크다고 한다.- named
- 적은 수의 이미지를 자주 사용할 때 적합
- imageWithContentsOfFile
- 많은 수의 이미지를 다룰 때 적합 (콜렉션뷰 마다 이미지뷰)
- named
출처
Extreamly memory usage when picker image from UIImagePickerViewController?
Using PHPickerViewController Images in a Memory-Efficient Way
https://developer.apple.com/forums/thread/684101
https://swiftsenpai.com/development/reduce-uiimage-memory-footprint/
'만난 에러들' 카테고리의 다른 글
Comments