먹고 기도하고 코딩하라

iOS17에서의 UIGraphicsBeginImageContext 이슈 본문

앱/Swift

iOS17에서의 UIGraphicsBeginImageContext 이슈

사과먹는사람 2023. 12. 6. 00:52
728x90
728x90

한 줄 요약

iOS 17 이상에서 UIGraphicsBeginImageContext()를 시도할 때, 이미지 사이즈가 (0, 0)인 경우 강제종료될 수 있으므로 UIGraphicsImageRenderer를 쓰는 것이 권장된다.

 

 

메이저 앱 배포 후 갑자기 ImageContext 관련해서 크래시리틱스에 이슈가 많이 나왔다.

**NSInternalInconsistencyException - UIGraphicsBeginImageContext() failed to allocate CGBitampContext: size={0, 0}, scale=1.000000, bitmapInfo=<address>. Use UIGraphicsImageRenderer to avoid this assert.**

 

이런 에러 메시지가 포함되어 있었는데, 콜스택이 특이했다. 내 경우엔 버튼의 isHighlighted가 set되는 걸 시작으로 타고타고 문제의 근본으로 내려가다보니 버튼의 이미지뷰 alpha 값을 조정하는 코드까지 내려갔다. 하지만 버튼의 isHighlighted가 정확히 어디서 set되는지는 콜스택에 없었다.

이럴 때는 Sentry에서 같은 에러 페이지를 열어놓고 좀 더 자세히 살펴보는 것이 도움이 된다. 센트리 에러의 Breadcrumbs를 보면 에러 전까지 어떤 타입의 이벤트들이 일어났는지 살펴볼 수 있다.

 

여러 케이스의 Breadcrumbs를 확인해보니 공통적으로 콘텐츠 다운로드를 하고 있었다는 것을 추리할 수 있었다.

다운로드라면 3.0.0 메이저 버전 업데이트에 새로 포함된 버튼에 심긴 기능이었다.

그래서 똑같이 콘텐츠 다운로드를 해보는데, 다운로드 중 버튼을 다시 누르는 순간 앱이 종료되었다.

 

왜 이런 일이 생겼을까? 우리 앱에서는 버튼을 커스텀 클래스로 만들어서 사용하고 있는데, 이 버튼 인스턴스를 탭하는 순간 isHighlighted가 set되는 것이 원인이었다.

서브 뷰들 중 버튼들을 확인해 isHighlighted 속성을 변경하는데, 그 여러 버튼들 중 하나가 size가 (0, 0)이어서 UIGraphicsBeginImageContext로 이미지 컨텍스트를 만드는 데에서 에러가 난 것으로 보인다.

더 특이한 건 이 에러는 iOS 17 이상의 폰에서만 재현된다는 것이었다. 센트리에서도 os를 확인해보니 전부 iOS 17 이상이었고, iOS 15 테스트폰으로 테스트했을 때는 강종되지 않았고 iOS 17로 업데이트한 개인 폰에서만 강종됐다.

이걸 왜 몰랐을까 생각해보니, 개발하고 QA가 있었던 시기는 올해 5월로 아직 iOS 17이 출시되기 이전이었다. 그래서 현상이 발생할 수 있다는 사실조차도 몰랐던 것 같다.

 

검색해보니 Apple Developer Forums에 Xcode 15 시뮬레이터에서만 크래시가 발생한다는 글을 확인했는데, UIGraphicsBeginImageContext(_:) 문서로 넘어가보니 이미 Deprecated된 지 오래고, iOS 2.0-17.0 으로 기입되어 있었다.

 

에러 메시지에도 적혀 있듯, UIGraphicsBeginImageContext 대신 UIGraphicsImageRenderer를 쓰는 것이 해결법이 될 수 있다.

UIGraphicsImageRenderer는 iOS 10 이상부터 지원하고 있으므로 앞으로 이미지 조작을 위해 새로 코드를 작성할 때는 UIGraphicsBeginImageContext 대신 UIGraphicsImageRenderer를 사용하는 것이 더 안전할 것으로 예상된다.

 

원래 이미지에 알파값을 적용하는 코드는 이렇게 되어 있었는데,

UIGraphicsBeginImageContextWithOptions(size, false, scale)
draw(at: .zero, blendMode: .normal, alpha: alpha)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage ?? UIImage()

이렇게 변경하면 된다.

return UIGraphicsImageRenderer(size: size).image { _ in
    draw(at: .zero, blendMode: .normal, alpha: alpha)
}

그러면 iOS 17에서도 정상적으로 수행이 가능하다.

 

그런데 애초에 size가 (0, 0)인 버튼이 있지 않은데 왜 이런 이슈가 나왔는지 이해할 수가 없다…. 물론 어떤 케이스인지 찾아낼 수 있다면 size가 0이 되지 않도록 변경하거나 size를 검사해서 예외인 경우에는 따로 처리하는 방법도 있다.

하지만 이 경우에는 버튼을 찾아내는 것보다 extension 메소드를 변경하는 것이 더 옳은 방향이라고 생각했다. 그래야 혹시 모를 예외 케이스가 발생하더라도 강제종료되는 것은 막을 수 있을 것이다.

 

Image Resize와 관련해서 UIGraphicsBeginImageContext, UIGraphicsImageRenderer를 비교한 블로그 글을 봤다.

메모리 사용량과 연계해서 설명하신 점과 글 내용, 예제가 좋아서 링크한다.

 

[Swift] Image Resize

이미지가 커서 메모리를 많이 잡아먹다보면 메모리부족으로 앱이 죽어버리는 경우가 생기죠..! 이러한 현상을 줄이고자 이미지 사이즈를 줄여서 다시 만들어내는데 그 작업을 해볼까해요 이미

nsios.tistory.com

 

 

 

728x90
반응형
Comments