본문 바로가기

iPhone

아이폰의 모델 뷰 컨트롤러 (MVC)

모델 
 모델의 메소드는 데이터를 제공하는 기능을 지닌 프로토콜과 컨트롤러에서 실행할 콜백 메소드를 구현해 데이터를 공급하는 역할을 한다. 

- 데이터 소스
 다른 객체가 필요로 하는 데이터를 가공하는 객체를 데이터 소스라고 한다. 기본적으로 내용없이 컨테이너 역할만 하는 UI객체도 있다. dataSource프로퍼티를 대입하거나 [uiobject setDataSource:applicationobject] 같은 호출을 통하여 데이터 소스로 사용할 객체를 설정하면 UI 객체(뷰)가 데이터 소스(모델)에 데이터를 요청할 수 있다.[각주:1] 일반적으로 데이터 소스는 로컬 데이터베이스 같은 파일, XML 피드 같은 웹서비스, 그 밖에 기기가 활용할 수 있는 소스에 데이터를 가져온다. UITableView와 UIPickerView는 데이터 소스를 지원하거나 필요로하는 몇 개의 코코아 터치 클래스 중 일부이다.
 데이터소스는 내부에서 다른 객체의 메소드를 구현해야 한다는 점에서 델리게이트와 비슷하다.[각주:2] 데이터 소스는 사용자의 상호작용에 반응하기보다는 객체를 생성하거나 제공해야 한다는 면에서 델리게이트와 다르다. 
 
- UIApplication 객체  
 에플 SDK에서 모든 프로그램은 [UIApplication sharedInstance]를 통해 참조할 수 있는 UIApplication 인스턴스를 딱 하나씩만 지닐 수 있다. 사파리에서 URL을 열거나, 키 윈도우를 복구하거나, 상태 바의 외형을 조정해야 할 필요가 없다면 UIApplication을 완전히 배제해도 상관없다. 어플리케이션을 시작할 때 설정 작업을 수행하고, 어플리케이션을 종료할 때 마무리 작업을 담당하는 별도의 어플리케이션 델리게이트 클래스를 중심으로 프로그램을 개발하는 것이 좋다. 


뷰 
 뷰를 구성하는 요소들은 UIView 클래스를 상속한 클래스들이고, 뷰와 연결한 UIViewController클래스가 뒷받침 한다.

- UIView
 UIView는 부 클래스를 대표하는 추상클래스이다. 사용자 인터페이스 클래스는 대부분 UIView와 UIView의 부모인 UIResponder에서 상속받으며, 뷰는 어플리케이션을 구성하는 모든 시각적 요소를 재공한다.[각주:3] UIView의 일종인 UIWindow클래스는 어플리케이션을 표시할 영역을 제공하고 기본적인 화면표시 기능을 제공한다. 뷰는 화면에 나타나야 함으로 모든 뷰는 일종의 프레임[각주:4]을 형성한다. 뷰는 다양한 하위 뷰로 이루어진 트리 형태의 계층구조를 지닌다. 부모자식간의 관계를 설정해주는 addSubView 메소드를 이용하여 윈도우나 다른 뷰에 추가로 화면을 표시할 수 있다. 

- UIViewController
 UIViewController 클래스는 컨트롤러라는 이름이 붙어있지만 MVC에서 컨트롤러의 역할을 하지 못한다. 컨트롤러라기보다는 뷰 핸들러, 모델의 역할을 한다. UIViewController는 방향전환 기능과 뷰 크기조절 기능이 있는데, 이것은 사용자가 아이폰을 돌렸을 경우 자동으로 화면을 회전시키며 네비게이변 바나 툴바 사용시 줄어드는 화면영역을 처리해 준다. UIViewController를 상속하는 모든 클래스는 순차적으로 동작하는 loadView 메소드를 구현하거나 .xib 파일에서 이미 만들어진 인터페이스를 가져오고 viewDidLoad를 호출하는 메소드를 구현해야 한다. 여기서 구현하는 메소드는 컨트롤러의 메인 뷰 구성을 담당하는 메소드이다. 인터페이스 빌더에서 미리 설정하지 않았다면 이 메소드에서 콜백, 델리게이트를 설정 할 수도있다.
 UIViewController는 뷰를 표현하는 방식과 상호작용을 해석하는 방식을 연결해주는 컨트롤러의 역할을 한다. 그리고 거의 모든 콜백을 UIViewController 자신에게 보내기 때문에 뷰를 생성하고 표시하는 컨트롤러라는 근본적인 역할 외에는 종종 모델의 역할을 하기도 한다.[각주:5]
 


컨트롤러
  MVC의 경우는 행을 선택하거나 버튼을 누르는 동작은 프로그램상 아무 의미가 없다. 의미가 들어있는 모델을 제공하는것은 개발자의 몫이다. 아이폰 SDK에 들어있는 코코아 터치 클래스는 다른 클래스로 메시지를 전달할 수 있는 몇가지 방법을 제공한다. 그중 가장 중요한 방법 세 가지는 
델리케이션, 타켓-액션, 노티피케이션이다. 

- 델리게이션
 수많은 UIKit 클래스는 델리게이션을 사용해 사용자의 상호작용에 응답할 책임을 자신의 델리게이트에 떠넘긴다. 객체에 델리게이트를 설정하면 모든 상호작용 메시지는 델리게이트로 전해지고 델리게이트가 모든 책임을 떠맡는다. 

 ex) UITableView
  UITableView에는 사용자가 테이블의 열을 텝 하였을 때 대응하는 방법이 들어가 있지 않다. 이 클래스는 범용  클레스이고, 탭 자체는 사실 아무런 의미가 없다. 뷰보다는 델리게이트를 찾아보고 델리게이트 메소드를 이용해 변경 사항을 전달한다. 테이블의 구현과는 상관없이 델리게이트를 설정한 후에 탭에관한 의미있는 동작을 추가할 수 있다. 


 델리게이션을 활용하면 나중에 어플리케이션이 사용할 핸들러를 추가한다는 전재하에 의미를 지니지 않은 클래스를 생성 할 수 있다. 

ex) UITableView의 델리게이트 메소드인 tableView:didSelectRowAtIndexPath:
 위의 메소드를 이용해 모델에 해당하는 행의 변경에 대한 응답방식을 구현해야 한다. 메뉴를 표시하거나, 하위 뷰로 넘어가거나, 현재 선태한 행 옆에 채크 마크를 표시할 수 있다. 대응 방식은 전적으로 행 선택 변경을 처리하는 델리게이트 메소드를 어떻게 구현하느냐에 달려있다.

**객체에 델리게이트를 설정하려면 델리게이트 프로퍼티에 대입하거나 setDelegate: 계열의 메소드를 사용해야 한다. 전자의 방법이 주로쓰이며, 이렇게 하면 어플리케이션은 상호작용 콜백을 델리게이트로 전송한다. 클래스 선언부에 델리게이트 프로토콜을 추가하여 객체가 델리게이트 호출을 구현한다는 사실을 엑스코드에 알려야 한다.  


- 타켓-액션
 하위 레벨에서 사용자의 상호작용을 재전송하는 방식이다. 이 방식은 거의 전적으로 UIControl 클래스의 자식 클래스에서만 볼 수 있다. 타겟-액션을 사용하면 컨트롤에 특정 사용자 이벤트가 발생했을 때 주어진 객체로 전달 하라고 지시할 수 있다.

ex) 사용자가 버튼을 눌렀을 경우 이벤트를 전달 할 다음 객체를 지정할 수 있다.
  아래의 코드는 UIBarButtonItem 인스턴스를 정의하는 코드이다. 버튼 항목의 타겟은 자신으로, 동작은 @selector(setHelvetica:)로 설정한다. 버튼을 텝하면 정의한 객체로 setHelvetica:메시지를 전달한다.


UIBarButtonItem *helvItem = [[[UIBarButtonItem alloc]initWithTitle:@"Helvetica" style:UIBarButtonItemStyleBordered target:self action:@selector(setHelvetica:)]autorelease];


setHelvetica:라는 메소드 이름은 임의로 정해도 상관없다 타켓-엑션은 델리게이트처럼 미리 메소드를 정의할 필요가 없다. 하지만 사용할 땐 똑같은 방식으로 작동한다. 사용자는 버튼을 누르는 등의 동작을 할 것이고, 타겟은 의미있는 응답을 제공하게 셀렉터를 구현해야 한다. 이와 같은 UIBarButtonItem 인흐턴스를 정의하는 객체라면 setHelvetica:메소드를 구현해야만 한다. (구현하지 않는다면 정의하지 않은 메소드를 호출했다는 에러가 발생하며 멈춰버린다.)
프로토콜을 필요로하는 델리게이트와는 달리, 타켓-액션의 경우에는 컴파일 시에 setHelvetica:에 대한 구현이 있는지 확인하지 않기 때문에 콜백이 정말로 존재하는 메소드를 참조 하는지 화인해야 하는것은 프로그래머의 몫이다. 


 기본적인 타켓-엑션은 0에서 2개사이의 인수를 전달한다. 이 인수는 상호작용할 객체와 사용자의 입력을 나타내는 UIEvent 객체다. 셀렉터를 통해 전달 인수를 선택 할 수 있으며 위에서 예로든 코드에서는 눌린 버튼의 UIBarButtonItem 인스턴스를 하나의 인수로 사용한다.

- 노티피케이션 
 노티피케이션을 사용하면 어플리케이션내의 객체끼리 통신할 수도 있고 시스템항의 다른 어플리케이션과 통신을 주고 받을 수도 있다.  객체는 '상태가 바뀌었음', '작업을 시작하겠음', '작업을 끝냈음' 같은 상태 메시지를 노티피케이션으로 브로드캐스팅할 수 있다.[각주:6] NSNotificationCenter 클래스는 어플리케이션 내부 노티피케이션의 표준이다. 이 노티피케이션 센터를 통하여 원하는 노티피케이션을 구독할 수도 있고 객체가 서로 통신하려고 보내는 노티피케이션을 수신할 수도 있다. 


정리 
 모델 : 데이터를 공급하는 기능 (컨트롤러와 데이터를 연결하는 역할)
 뷰 : 화면을 구성하는 기능
 컨트롤러 : 컨트롤러의 동작은 델리케이션, 타켓-액션, 노티피케이션 세가지 핵심 기술로 이루어져 있다. 
                 ( 뷰에서 일어나는 동작에 대한 정보를 전달이라고 이해했지만... 정확하지 않다. ㅠㅠ)


 참고서적 : The iPhone Developer's Cookbook / 에리카 세든 지음 / 에이콘
  1. ex) UITableView가 자신이 사용할 테이블 셀을 요정하는것과 비슷하다. [본문으로]
  2. 테이블이 들어있는 UITableViewController가 전형적인 예이다. [본문으로]
  3. UIView의 중요 클래스는 UIImageViews, UIAlertView 등이 있다. [본문으로]
  4. 프레임은 화면에서 각 뷰가 차지할 공간을 정의하는 사각형 모양의 영역이다. 이 사각형은 뷰의 기준점과 크기를 바탕으로 만들어진다. [본문으로]
  5. MVC 패러다임을 벗어나는 특징이지만 편리하고 프로그래밍하기 쉽다는 장점이 있다. [본문으로]
  6. 객체는 브로드케스팅 메시지를 수신할 수도 있고 무시할 수도 있다. 노티피케이션을 수신하려는 객체는 노티피케이션 센터에 등록하고 메시지를 기다려야 한다. [본문으로]