-
무엇을 참조하고 있는지 정확히 파악하자일상, 생활 기록 2020. 8. 8. 01:44
개요
참조라는 개념은 프로그래밍 전반적으로 쓰이는 단어입니다. 예를 들면 변수가 특정 객체를 가르키고 있을 수도 있고, 또는 변수가 또 다른 변수(리스트와 같은)를 가르키고 있을 수도 있죠.
이 참조라는 개념이 정확하게 정립이 되지 않으면 디버깅하는데 많은 어려움을 겪을 수 있습니다. 널포인터익셉션과 같은 버그나 여러 잡다한 버그들이 이 참조라는 부분에서 실수가 나기 때문이죠. 그래서 변수나 객체를 선언할 때 참조를 반드시 신경써야겠습니다.
최근 Behind라는 숭실대학교 IT 동아리인 유어슈에서 진행중인 프로젝트가 있습니다. 대학 강의 별로 커뮤니티, 게시판을 만들어서 강의별 여론을 활성화하자는 목적을 가진 앱이죠.
저는 이 앱을 제작하던 중 참조와 관련한 사소한 버그를 만납니다. 하지만 뭐가 문제인지 몰라 많이 해멨습니다.
Recyclerview의 NotifyDataSetChanged 메소드
댓글을 불러오는 api를 연결하고 있었습니다. 어댑터를 초기화하는 시점과 서버와 연동해 댓글 데이터를 가져오는 시점이 일치하지 않기 때문에 Retrofit2를 사용해 Response를 받으면 뷰모델의 List 데이터를 업데이트 해주고 다시 엑티비티에서는 이 List 데이터를 어댑터의 updateList()메소드를 불러주는 형태입니다. updateList()는 다음과 같습니다.
fun updateList(comments: List<ResponseCommentDto>) { this.comments.clear() this.comments.addAll(comments) notifyDataSetChanged() }
this.comments는 adapter 클래스가 가지고 있는 변수입니다. 그런데 이 함수를 호출해도 Adapter는 액션이 없었습니다. 뭐가 문제일지 찾는 도중 아래와 같은 글을 발견합니다.
One of the main reasons notifyDataSetChanged() won't work for you - is,
Your adapter loses reference to your list.
When you first initialise the Adapter it takes a reference of your arrayList and passes it to its superclass. But if you reinitialise your existing arrayList it losses the reference, and hence, the communication channel with Adapter.
When creating and adding a new list to the Adapter. Always follow these guidelines:
-
Initialise the arrayList while declaring it globally.
-
Add the List to the adapter directly with out checking for null and empty values . Set the adapter to the list directly (don't check for any condition). Adapter guarantees you that wherever you make changes to the data of the arrayList it will take care of it, but never loose the reference.
-
Always modify the data in the arrayList itself (if your data is completely new than you can call adapter.clear() and arrayList.clear() before actually adding data to the list) but don't set the adapter i.e If the new data is populated in the arrayList than just adapter.notifyDataSetChanged()
Stay true to the Documentation.
정말 좋은 글인데요. adpter가 list에 대한 참조를 잃지 마라는 말이었습니다. 참조라는 데서 힌트를 얻고 혹시 참조가 다르진 않은지 여기저기 찾아봤습니다. 그런데 list에 대한 참조가 변함이 있는 것이 아니라 애초에 Recyclerview의 setAdatper로 넣어주는 adapter와 실제로 notifyDataSetChanged하는 어댑터가 다르더군요.
class ArticleActivity: AppCompatActivity() { ... private var adapter : ArticleAdapter = ArticleAdapter((mutableListOf())) override fun onCreate(savedInstanceState: Bundle?) { ... } private fun initRecyclerView() { binding.articleCommentRecyclerView.run { layoutManager = LinearLayoutManager(this@ArticleActivity, RecyclerView.VERTICAL, false) adapter = this.adapter } }
보이시나요? run 함수를 사용해 articleCommentRecyclerView의 Adapter에 adapter 변수를 넣어주고 있습니다. 저는 당연히 엑티비티에서 정의된 전역 변수인 adapter를 넣고 있는줄 알았습니다.
하지만, 저기 this.adapter는 articleCommentRecylcerView의 adapter였습니다. 왜냐하면 run은 수신객체지정람다 함수이기 때문이죠! 그래서 저 코드를 다시,
class ArticleActivity: AppCompatActivity() { ... private var adapter : ArticleAdapter = ArticleAdapter((mutableListOf())) override fun onCreate(savedInstanceState: Bundle?) { ... } private fun initRecyclerView() { binding.articleCommentRecyclerView.run { layoutManager = LinearLayoutManager(this@ArticleActivity, RecyclerView.VERTICAL, false) adapter = this@ArticleActivity.adapter } }
이렇게 코드를 수정하니 모든게 잘 작동하기 시작했습니다. 즉 다른 변수를 참조하고 있었던 것이네요.
'일상, 생활 기록' 카테고리의 다른 글
2020년 회고 (1) 2021.01.02 반응형 UI 설계를 위한 덕목 - 가변 영역, 고정 영역 구분 (0) 2020.09.05 개인정보처리방침 - 군머니티 (0) 2020.07.16 초보의 정규표현식 배우기 (0) 2020.05.12 첫 코딩 테스트 후기 (0) 2020.05.11 -