본문 바로가기

Software/iOS & Objective-C

객체간을 여행하는 데이터를 위한 안내서

본 글은 http://www.iphoneos.co.kr/zbxe/dev/13845 
웹 사이트의 Delegate님의 글을 옴겨온 것입니다.

제목을 "은하수를 여행하는 히치하이커를 위한 안내서"를 패러디 해봤습니다. :) OOP에서 객체간의 데이터 교환은 매우 중요한 한 부분인 것 같습니다. 이 부분을 잘못 접근하게 되면 이탈리안 레스토랑을 차려도 될 만큼 많은 스파게티를 만들어 낼 수도 있고 아이폰에서는 밧데리를 많이 소모시켜서 지구 환경에 영향을 줄 수도 있습니다. 물론 정신 건강에 매우 해로워 정신과 의사들이 어부지리를 얻게 할 수도 있을지 모릅니다. 여튼, 객체간의 잘못된 만남은 남녀간의 잘못된 만남 만큼이나 슬픈 결말로 치닫는 경우를 많이 봐 왔습니다.


일단 어떤 방법을 통해서 객체간에 데이터를 주고 받을 수 있는지 몇가지를 늘어놓아보겠습니다.


1. 먼저 가장 무식한 방법으로 객체를 서로 참조 시키는 방법입니다. #import 또는 @class로 클래스를 알리고 그 것으로 변수를 만들어서 해당 인스턴스의 포인터 값을 넣고 사용하는 방법입니다. 너가 나를 알고 내가 너를 알면 못할게 뭐가 있겠냐는거죠. 좋습니다. 하지만 이게 무슨 소셜 네트웍 서비스도 아니고 1촌들이 많아지면 감당할 수 없는 지경까지 이르게 됩니다. 서로 서로 손에 손을 잡고 부둥켜 안고 떨어질래야 떨어질 수 없는 한 무리의 조직을 이루는 순간 그 안에서 버그도 함께 커 나가겠죠. 물론 코드를 읽는 사람은 머리를 쥐어 뜯을게 분명합니다.


2. 1번의 방법을 조금 세련되게 바꾸는 방법이 있습니다. 프로토콜 또는 그 유사한 형태를 사용하는 것이죠. 이것은 나는 너는 모르겠고 하여튼 나와 이야기 하기 위해서 약속한 내용만 가지고 이야기 하겠다는 것입니다. 즉, 데이터를 주고받는 형태만 정의해두고 서로 주고 받겠다는 것이지요(프로토콜). 코코아에서 딜리게이트, 데이터소스, 타겟/액션 같은 것들이 이 부류에 속합니다.

 

1번 방법에 비해서 진보하긴 했지만 여전히 문제점을 안고 있습니다. 가끔, retain을 잘못 걸어서 크로스 래퍼런스 또는 서큘러 래퍼런스(서로를 retain 걸면 그 객체들은 영원히 사라지지 않습니다.)를 하는 경우가 생기기도 하고 retain을 걸지 않아서 문제를 일으키기도 합니다.(retain을 걸지 않았으면 그 객체가 소멸되었으나 포인터 값은 유지되겠죠.) 여전히 둘 사이에 어느 정도 밀접한 상관관계를 맺고 있는 상태가 되기도 하구요.


여기서 딜리게이트, 데이터소스, 타겟/액션에 대해서 잠깐만 곁다리로 빠질께요.

딜리게이트는 말 그대로 위임을 말합니다. 기본적으로 이 개념은 내가 처리해야 할 일인데 다른 객체에게 잠시 떠 맏기는 것입니다. 떠맏긴다는 표현이 조금 어색하다면 내 상태를, 또는 내 동작을 어떻게 할 것인지 다른 객체에 물어보는 경우도 해당 되겠지요.

 

컨트롤러의 특징 상 주로 컨트롤러가 딜리게이트의 역할을 많이 하게 됩니다. 앱 컨트롤러는 앱의 딜리게이트 역할을 뷰 컨트롤러는 뷰의 딜리게이트 역할을 하는 것이지요. setDelegate같은 메소드로 딜리게이트로 등록하게 되면 상대쪽에서는 딜리게이트를 받아서 retain을 하지 않습니다. 즉, 그 객체가 계속 유지될 것이라는 무언의 약속을 하는 것이지요. 만일 실행중에 setDelegate:nil을 상대에게 보내주지 않고 소멸된다면 상대쪽에서는 엉뚱한 포인트의 값에 접근하게 됩니다.

 

데이터소스는 데이터를 제공해주는 객체입니다. 역시 setDataSource같은 류의 메소드를 이용해서 상대 객체에 세팅을 해주게 되는데 그러고 나면 상대 객체가 데이터가 필요한 경우 이쪽으로 프로토콜로 사전에 정해진 메소드를 호출해서 데이터를 가져갑니다. 테이블 뷰의 경우를 생각하시면 될 것 같습니다. retain문제는 역시 딜리게이트와 동일합니다.

 

타겟/액션 역시 비슷한 구조이긴 한데 이 경우는 Control에 제한됩니다. 즉, 컨트롤에 뭔가 반응이 생기면 사전에 등록해둔 메소드를 불러 주는 것이지요. 앞의 두 경우가 프로토콜 같은 것으로 메소드를 공유하는 것과 달리 이 경우는 셀렉터를 상대에게 등록하는게 다르다고 할 것입니다. 하지만 기본 개념 자체는 유사하다고 볼 수 있구요.

 

다시 이야기가 돌아와서 이 세가지 경우를 그냥 딜리게이트라고 편의상 부르도록 하겠습니다. 딜리게이트의 경우 상대방의 포인트를 기억하고 있다는 점에서 완전이 뚝 떨어진 느낌은 들지 않습니다. 그 이유는 결국 두 객체간을 연결 시키기 위해서 어느 한쪽이 상대를 알고 등록하는 과정을 거쳐야 한다는 것이지요. 하지만 1번의 경우에 비해서 스파게티가 될 확률은 많이 줄어들었습니다.


3. 마지막 방법은 노티피케이션이라는 것입니다. GoF의 디자인 패턴에도 거의 비슷한 패턴이 옵저버라고 있습니다. 이 방법은 둘간의 유착관계를 더욱 때내기 위해서 중간에 다른 객체가 개입하는 것입니다. 즉, 전달자가 따로 있는 것이지요. 객체는 상대방을 모른체 전달자와만 이야기를 하게 됩니다. 코코아에서는 그 전달자가 NSNotificationCenter입니다.

 

쉽게 생각해보기 위해서 NSNotificationCenter를 방송국이라고 해보죠. 한 사람이 방송국에 제보를 합니다. 그 내용은 곧 기사가 되어 다른 사람들에게 브로드캐스트 되고 만일 그 기사에 관심이 있는 사람이고 방송을 보고 있었다면 그 내용을 알게 되는 것입니다. 즉, 제보자와 방송을 본 사람은 전혀 이전에는 알지 못했던 사람이라는 것이지요. 하지만 정보는 전달 되었습니다.

 

NSNotificationCenter는 정보 뿐만 아니라 노티피케이션을 보낸 객체 자체를 받는 객체로 보내버리기도 합니다.(id로)

 

이 방법은 사전에 내가 특정한 정보에 관심이 있고 그 정보가 뜨면 특정 메소드를 불러달라는 것을 addObserver를 이용해서 NotificationCenter에 등록해하고 보내고 싶을 때는 특정 정보끼리 동일한 이름을 부여하여 postNotification함으로써 가능해집니다.


너무 많은 내용을 짧게 쓸려다보니 저도 정신이 없네요.

다시 핵심으로 돌아가서 그렇다면 언제 무엇을 쓸 것인지 정리해보도록 할께요. 일단 1번 방법은 되도록이면 안쓰는 것이 좋겠죠. 뭐 구차한 설명 더 안붙일께요.

 

그렇다면 2번과 3번 방법인데요, 분명 2번 방법이 효율성이 좋기는 합니다. 하지만 지나치게 많아지면 분명 1번 방법 보다는 낫지만 코드를 복잡하게 합니다. 게다가 상관관계가 멀리 떨어진 객체간의 간헐적인 데이터 교환의 경우 불리한 점도 있죠. 그렇다면 답은 간단하게 나옵니다.


프래임웍에서 통상적으로 딜리게이트/데이터소스/액션-타겟을 쓰는 부분은 그대로 하시면 됩니다.

 

그 외에 데이터의 잦은 교환이 요구되는 경우 딜리게이트나 데이터소스(뭐 결국 그게 그거 아닌가 싶지만)를 선택하시면 됩니다.

 

서로 크게 상관없던 객체끼리 잦은 데이터 교환이 아니고 한쪽에서 다른 쪽으로 단방향으로 보내고 또 여러 객체에 보낼때는 노티피케이션을 선택하시면 편합니다.

 

그렇다고 해서 어떤 경우에 꼭 어떤 방법을 선택해야 하는 것은 아닙니다. 개발자의 융통성이 필요한 부분입니다. 경우에 따라서 제대로 된 선택을 하는 것은 결국 개발자의 몫입니다.


장황한 글 읽어주셔서 감사합니다.


그럼..

 

반응형

'Software > iOS & Objective-C' 카테고리의 다른 글

iPhone Application Life cylce  (0) 2013.05.16
About...Notification...!!!  (0) 2013.04.23
아이폰에서 iOS 앱 테스트 하기  (0) 2013.04.02
property의 역사를 통한 고찰  (0) 2013.02.25
Objective C 기초정리  (0) 2013.01.23