[iOS] SwiftUI 채팅화면 MainTread 락현상 수정하기
안녕하세요 물먹고하자 입니다 :)
미뤄왔던 업무중에 SwiftUI쪽 채팅화면이 어느순간부터 느려지고 있어서 개선하고자 합니다.
예전에 작성했던 글 : [SwiftUI 공부] 그룹웨어 채팅 Objc -> Swift, SiwftUI 적용기
2022년도에 변경하고, 최적화는 추가적으로 진행하지 못했었네요 ㅠㅠ
일단 바로 시작!
1. 원인을 찾아보자
💡수정전.gif 부분에서 스크롤시 MainThread 락현상이 보임. 원인을 찾아보자!
2. 말풍선에 사용되는View를 기본 Text 바꿔보자
💡 기존에 말풍선 부분을 Text(임시)로 변경시 MainThread 락현상이 보이지 않았음.
- API 통신부분에 의해 느려지는건 아님.
- ScrollView + LazyVStack에 문제가 있는게 아님.
- 말풍선영역에 대한 문제가 있어 보임. (여기다!!)
3. 말풍선을 그릴때 어떤 문제인지 확인해보기
말풍선 부분을 직전 2022년도에 작업을 진행할때 View를 나눠서(정리)개발을 하였는데,
각각의 View를 EnvironmentObject로 사용하여 개발을 했었다.
당시에는 EnvironmentObject 몰라서 사용했었는데, 채팅 특성상 해당화면을 다른곳에서 쓸일이 없더라구요.
StateObject나 아니면 ViewBuilder 함수화 해서 사용해도 상관없을 화면들로 판단되었습니다.
3개다 사용해도 문제가 없는데, 메모리 이슈라고 판단 이들어 ChatGPT 궁금증 문의!
💡 ChatGPT 검색후 도움건 (EnvironmentObject 관련 검색)
- Observed Updates: EnvironmentObject는 데이터가 변경될 때마다 뷰가 다시 그려지게 하는데, 여러 개의 객체가 동시에 업데이트되면 UI 스레드에서의 부하가 증가해 렌더링 속도가 느려질 수 있습니다.
- Dependency Chains: EnvironmentObject가 중첩되거나 서로 참조하는 경우, 그 의존 관계가 복잡해져서 일부 업데이트가 불필요하게 발생할 수 있습니다. 이로 인해 불필요한 리렌더링이 발생하면서 성능에 영향을 줄 수 있습니다.
- 메모리 사용: EnvironmentObject 자체가 메모리를 크게 차지하지는 않지만, 그 안에 저장된 데이터의 크기와 해당 데이터를 참조하는 뷰 계층의 깊이에 따라 메모리 사용이 증가할 수 있습니다. 특히, 상태가 여러 뷰에서 참조될 경우 시스템이 관리하는 상태의 수가 늘어나면서 메모리 관리 효율이 떨어질 수 있습니다.
음… 제 얘기하는줄… 계층으로 값 전달하고 있었다..
말풍선 화면을 그릴때 ScrollView + LazyVStack 에서 itemList[item]을
ㄴ 1) 껍데기View environmentObject(item) 전달
--ㄴ 2) 껍데기View에서 타입별 말풍선화면 구분 각각의 말풍선에 environmentObject(item) 전달
----ㄴ 3) 각각의 말풍선 중에 또 답글이거나, 다른 특수한 상황일때 environmentObject(item) 전달
거기에 Published itemList 여서 자동갱신시점이 더 많을 수 밖에 없는 환경
추가로 EnvironmentObject을 StateObject 로 변경할껀데 대응이 맞는지 검색해보기
💡StateObject, ObservedObject 관련 검색!
- StateObject
- 용도: 해당 객체의 소유자가 되고, 초기화와 생명주기를 관리할 때 사용합니다.
- 상황: 최상위 뷰나 해당 객체를 처음 생성하는 뷰에 사용합니다.
- 생명주기: @StateObject로 선언된 객체는 뷰가 다시 그려지더라도 한 번만 생성되며, 뷰가 메모리에서 사라질 때 해제됩니다.
- ObservedObject
- 용도: 객체를 소유하지 않고, 이미 외부에서 소유하고 있는 객체에 의존할 때 사용합니다.
- 상황: 상위 뷰에서 생성된 객체를 하위 뷰에서 구독할 때 사용합니다.
- 생명주기: @ObservedObject는 객체를 생성하거나 관리하지 않으므로, 뷰가 다시 그려질 때마다 새롭게 할당되지 않습니다. 단순히 상위 뷰로부터 전달된 객체의 변경을 반영할 뿐입니다.
그렇다면 제 상황에서는 사실 객체를 전달할 필요까지는 없어서
EnvironmentObject를 ViewBuilder로 내부 뷰 함수로 만들고,
어쩔수 없는경우에는 ObservedObject로 변환 하여 사용해야겠다는 판단이 드네요!
💡 작업후… EnvironmentObject을 사용했을때 보다는 조금 나아진정도?
드라마틱한 효과로 보이진 않았다 ㅠㅠ
4. 그렇다면.. 말풍선을 만드는 객체가? 문제가 있나
💡위에까지 실험 후 말풍선을 그릴때 Text영역(Attribute정보)를
그려주기 위한 설정값(Option)을 만다는 부분을 시간을 체크
- 설정값(Option)을 생성하는 행위가 각각 0.3밀리초 만큼 걸리는 현상을 확인
각각의 말풍선마다 멘션, Custom링크들 처리들이 있어서 Text 표현시 AttributeString 처리하고 있습니다.
위의 이미지는 해당 함수부분
문제로 생각되는 부분은 함수 내부의 왼쪽, 오른쪽 말풍선에 따른 각각의 색상처리를 하기위한 Option객체!!
생각해보면 저 Option객체가 왼쪽1개, 오른쪽1개 거의 고정이였는데, 왜 텍스트 값 뽑을때마다 생성했는지 모르겠내요 ㅠㅠ (실수)
[Swift 공부] 구조체 vs 클래스 (Struct vs Class) 예전에 객체 생성실험도 했었네요
기능상으론 동일하지만, 생성부분을 1번만 전역으로 참조해서 쓰는형식으로 수정결정!
수정 전 속도체크하면 빨간네모참고 Option 객체 생성시마다 0.021초씩 누적되는 현상 확인
수정 후 호출되는 위치는 같지만, 생성을하지 않다보니 여러번 호출되도 시간초가 차이가 나지 않음.
마무리
앞서 SwiftUI로 채팅부분을 만들때 SwiftUI를 처음 도입하기도 했고, 지식을 많이 얻지 못한상태에서 개발했었는데 나쁘지 않았던 결과였습니다.
하지만 역시나 딱히 코드개선없이 기능만 추가되다보니 어느순간 관리가 제대로 되지 않고 있었네요.
그래도 뒤늦게 나마 사용성 문제가 되는 부분을 고쳐서 다행이라고 생각하네요.
추가로 개선을 하면서 ScrollView + LazyVStack의 이슈건 Offset 이동이 불가능한 부분으로 인하여(자연스러운 스크롤 고정효과가 안됨)
최근에 iOS16 이상 UICollectionView + SwiftUI 버전으로도 추가 개선중입니다. 이부분은 작업 완료되면 공유드리겠습니다.
오늘은 이만!
즐거운 코딩되세요~
끝.
# __[iOS] SwiftUI 채팅화면 MainTread 락현상 수정하기__
안녕하세요 __물먹고하자__ 입니다 :)
미뤄왔던 업무중에 SwiftUI쪽 채팅화면이 어느순간부터 느려지고 있어서 개선하고자 합니다.
예전에 작성했던 글 : [[SwiftUI 공부] 그룹웨어 채팅 Objc -> Swift, SiwftUI 적용기](https://xodhks0113.blogspot.com/2022/07/swiftui-objc-swift-siwftui.html)
2022년도에 변경하고, 최적화는 추가적으로 진행하지 못했었네요 ㅠㅠ
__일단 바로 시작!__
---
## 1. __원인을 찾아보자__
> 💡수정전.gif 부분에서 스크롤시 __MainThread 락현상이 보임.__ 원인을 찾아보자!
---
## 2. 말풍선에 사용되는View를 기본 Text 바꿔보자
> 💡 기존에 말풍선 부분을 Text(임시)로 변경시 __MainThread 락현상이 보이지 않았음.__
- API 통신부분에 의해 느려지는건 아님.
- ScrollView + LazyVStack에 문제가 있는게 아님.
- __말풍선영역에 대한 문제가 있어 보임. (여기다!!)__
---
## 3. 말풍선을 그릴때 어떤 문제인지 확인해보기
말풍선 부분을 직전 2022년도에 작업을 진행할때 View를 나눠서(정리)개발을 하였는데,
각각의 __View를 EnvironmentObject__로 사용하여 개발을 했었다.
당시에는 __EnvironmentObject__ 몰라서 사용했었는데, 채팅 특성상 해당화면을 다른곳에서 쓸일이 없더라구요.
__StateObject__나 아니면 __ViewBuilder__ 함수화 해서 사용해도 상관없을 화면들로 판단되었습니다.
3개다 사용해도 문제가 없는데, 메모리 이슈라고 판단 이들어 ChatGPT 궁금증 문의!
> 💡 ChatGPT 검색후 도움건 __(EnvironmentObject 관련 검색)__
- Observed Updates: EnvironmentObject는 데이터가 변경될 때마다 뷰가 다시 그려지게 하는데, __여러 개의 객체가 동시에 업데이트되면 UI 스레드에서의 부하가 증가해 렌더링 속도가 느려질 수 있습니다.__
- Dependency Chains: EnvironmentObject가 중첩되거나 서로 참조하는 경우, 그 의존 관계가 복잡해져서 일부 업데이트가 불필요하게 발생할 수 있습니다. 이로 인해 __불필요한 리렌더링이 발생하면서 성능에 영향을 줄 수 있습니다.__
- 메모리 사용: EnvironmentObject 자체가 메모리를 크게 차지하지는 않지만, 그 안에 저장된 데이터의 크기와 해당 데이터를 __참조하는 뷰 계층의 깊이에 따라 메모리 사용이 증가할 수 있습니다.__ 특히, 상태가 여러 뷰에서 참조될 경우 시스템이 관리하는 상태의 수가 늘어나면서 메모리 관리 효율이 떨어질 수 있습니다.
__음... 제 얘기하는줄... 계층으로 값 전달하고 있었다..__
말풍선 화면을 그릴때 __ScrollView + LazyVStack 에서 itemList[item]__을
ㄴ 1) 껍데기View __environmentObject(item) 전달__
--ㄴ 2) 껍데기View에서 타입별 말풍선화면 구분 각각의 말풍선에 __environmentObject(item) 전달__
----ㄴ 3) 각각의 말풍선 중에 또 답글이거나, 다른 특수한 상황일때 __environmentObject(item) 전달__
거기에 __Published__ itemList 여서 자동갱신시점이 더 많을 수 밖에 없는 환경
추가로 EnvironmentObject을 StateObject 로 변경할껀데 대응이 맞는지 검색해보기
> 💡StateObject, ObservedObject 관련 검색!
- __StateObject__
* 용도: 해당 객체의 소유자가 되고, 초기화와 생명주기를 관리할 때 사용합니다.
* 상황: 최상위 뷰나 해당 객체를 처음 생성하는 뷰에 사용합니다.
* 생명주기: @StateObject로 선언된 객체는 뷰가 다시 그려지더라도 한 번만 생성되며, 뷰가 메모리에서 사라질 때 해제됩니다.
- __ObservedObject__
* 용도: 객체를 소유하지 않고, 이미 외부에서 소유하고 있는 객체에 의존할 때 사용합니다.
* 상황: __상위 뷰에서 생성된 객체를 하위 뷰에서 구독__할 때 사용합니다.
* 생명주기: @ObservedObject는 객체를 생성하거나 관리하지 않으므로, 뷰가 다시 그려질 때마다 새롭게 할당되지 않습니다. 단순히 상위 뷰로부터 전달된 객체의 변경을 반영할 뿐입니다.
그렇다면 제 상황에서는 사실 객체를 전달할 필요까지는 없어서
__EnvironmentObject__를 __ViewBuilder__로 내부 뷰 함수로 만들고,
어쩔수 없는경우에는 __ObservedObject__로 변환 하여 사용해야겠다는 판단이 드네요!
>💡 작업후... __EnvironmentObject__을 사용했을때 보다는 조금 나아진정도?
드라마틱한 효과로 보이진 않았다 ㅠㅠ
---
## 4. 그렇다면.. 말풍선을 만드는 객체가? 문제가 있나
>💡위에까지 실험 후 말풍선을 그릴때 __Text영역(Attribute정보)__를
그려주기 위한 설정값(Option)을 만다는 부분을 시간을 체크
- 설정값(Option)을 생성하는 행위가 __각각 0.3밀리초 만큼 걸리는 현상을 확인__
각각의 말풍선마다 __멘션, Custom링크들 처리__들이 있어서 Text 표현시 __AttributeString__ 처리하고 있습니다.
위의 이미지는 해당 함수부분
문제로 생각되는 부분은 함수 내부의 __왼쪽, 오른쪽 말풍선에 따른 각각의 색상처리를 하기위한 Option객체!!__
생각해보면 저 __Option객체가 왼쪽1개, 오른쪽1개__ 거의 고정이였는데, 왜 텍스트 값 뽑을때마다 생성했는지 모르겠내요 ㅠㅠ (실수)
[[Swift 공부] 구조체 vs 클래스 (Struct vs Class)](https://xodhks0113.blogspot.com/2019/09/swift-vs-struct-vs-class.html) 예전에 객체 생성실험도 했었네요
기능상으론 동일하지만, __생성부분을 1번만 전역으로 참조__해서 쓰는형식으로 수정결정!
수정 전 속도체크하면 __빨간네모참고__ Option 객체 생성시마다 0.021초씩 누적되는 현상 확인
수정 후 호출되는 위치는 같지만, 생성을하지 않다보니 __여러번 호출되도 시간초가 차이가 나지 않음.__
---
## __마무리__
앞서 SwiftUI로 채팅부분을 만들때 SwiftUI를 처음 도입하기도 했고, 지식을 많이 얻지 못한상태에서 개발했었는데 나쁘지 않았던 결과였습니다.
하지만 역시나 딱히 __코드개선없이 기능만 추가되다보니 어느순간 관리가 제대로 되지 않고 있었네요.__
그래도 뒤늦게 나마 사용성 문제가 되는 부분을 고쳐서 다행이라고 생각하네요.
추가로 개선을 하면서 __ScrollView + LazyVStack의 이슈건 Offset 이동이 불가능한 부분__으로 인하여(자연스러운 스크롤 고정효과가 안됨)
최근에 __iOS16 이상 UICollectionView + SwiftUI 버전__으로도 추가 개선중입니다. 이부분은 작업 완료되면 공유드리겠습니다.
오늘은 이만!
즐거운 코딩되세요~
끝.
댓글
댓글 쓰기