MVC 패턴에서 프로그래머가 가장 많이 작성하는 코드는 크게 두 가지인데, 하나는 모델 데이터에서 발생한 변경을 뷰에 반영하는 것이고, 다른 하나는 뷰에서 사용자의 조작에 의해서 변경된 값을 모델 데이터에 반영하는 것이다. 사실 이것이 MVC에서 컨트롤러가 수행하는 일이다. GUI프로그램을 작성할 때 가장 많이 하게되는 이러한 작업을 조금 더 간단하게 (가급적이면 코드를 작성하지 않고) 구현하는 방법이 있으면 제법 편하지 않을까? 이럴 때 사용하는 코코아 바인딩은 Cocoa에서 macOS에서 데이터 모델과 뷰 사이의 양방향 연결을 만드는 일종의 ‘마법’이다. 예를 들어 아래 그림과 같은 간단한 앱을 생각해보자.
코코아 바인딩 샘플 앱
이 앱은 매우 간단한 구조를 가지고 있다. 뷰에는 숫자를 표시하는 레이블하나와 슬라이더가 하나있다. 이 앱이 하는 일은 사용자가 슬라이더를 좌우로 옮길 때마다, 슬라이더의 값이 레이블에 숫자로 반영되는 것이다. 이를 위해서는 뷰 컨트롤러에 대해서 다음의 프로퍼티와 액션이 정의되고, 그에 대한 코드가 구현되면 된다.
뷰 컨트롤러는 숫자값 하나를 저장할 NSNumber타입의 프로퍼티를 하나 가지고 있다. (꼭 NSNumber가 아니어도 된다. 그냥 float이나 double 타입의 값이기만 해도 된다.)
뷰 컨트롤러는 텍스트레이블에 대한 아웃렛 연결을 가지게 된다. (이 아웃렛을 통해서 레이블을 참조하고 그 값을 변경할 수 있다.)
슬라이더는 사용자가 옮길 때마다 뷰 컨트롤러에게 액션 메시지를 보내게 된다. 이 메소드에서는 슬라이더의 값을 받아서 레이블의 값을 세팅하는 코드를 작성해주어야 할 것이다.
코코아 바인딩은 특정한 프로퍼티에 대해서 UI 컨트롤과의 양방향 연결을 구성하는 테크닉이다. 기본적으로 뷰 내의 컨트롤과 뷰 컨트롤러 사이의 연결을 만들 수 있지만, 반드시 뷰와 뷰 컨트롤러 사이의 관계일 필요는 없다. 샘플 프로젝트는 추가적인 클래스 없이 앱 델리게이트를 이용하기로 한다. 기본 macOS용 앱을 위한 프로젝트를 시작하고, 앱 델리게이트 파일을 다음과 같이 하나의 프로퍼티를 추가한다. (그외에 다른 아웃렛이나 액션을 선언하지 않아도 된다.)
Put Together
MainMenu.xib 파일을 열어서 메인 윈도우에 레이블 하나와 슬라이더 하나를 아래와 같이 배치한다. 참고로 슬라이더를 추가한 후에는 속성 인스펙터에서 Continuous에 체크한다. 슬라이더는 기본적으로 움직였다가 마우스를 릴리즈하는 시점에 타깃에게 액션 메시지를 보내는데, Continuous가 체크되면 슬라이더 노브를 움직이는 동안에 계속적으로 메시지가 전달된다. (즉 움직이는 중에 숫자값이 계속 바뀌는 것을 볼 수 있다.)
그리고 숫자값 프로퍼티 v에 대해 초기값을 설정해보자. 왼쪽 객체 목록에서 앱 델리게이트를 선택하고, 속성 인스펙터에서 User Defined Runtime Attributes 아래에 있는 + 버튼을 클릭한다. 키 패스에는 v를 입력하고 타입은 Number, 값은 50으로 입력한다.
참고로, nib 파일은 뷰, 컨트롤, 컨트롤러등의 여러 객체들이 어떠한 프로퍼티값을 가지고 있는 상태 그대로를 직렬화한 파일이다. 따라서 인터페이스 빌더에서 정의하는 크기와 위치(곧, 뷰의 frame 속성이다.)라던지 색상 및 컨트롤의 유형 등의 정보는 객체의 인스턴스를 만들어서 코드상에서 설정해야 하는 번거로움을 덜어주는 역할이라고 보면 된다.
이번에는 바인딩을 연결하자. 먼저 텍스트 레이블을 선택한다. 참고로 이 레이블에 대해서는 앱 델리게이트에서 어떤 아웃렛 연결이나 액션메시지 연결은 없었다는 점을 상기하자. 우측 인스펙터의 7번째 탭이 바인딩 탭이다. Value 라고 되어 있는 부분을 보면 몇 가지 값 속성이 있는데, 그 중 value 를 찾아서 세부 항목을 열어본다. 그러면 팝업박스를 통해서 어떤 객체에 연결할 것인지를 고를 수 있다.1 여기서 앱 델리게이트를 선택한다.
다음은 모델키패스(model keypath)를 입력해야 한다. 연결하고자 하는 숫자값은 앱델리게이트 내의 v 라는 이름을 가진 NSNumber이다. 이 키패스는 바인딩되는 객체를 기준으로, self.v 라고 입력한다. (self.을 생략하고 v만 입력해도 된다.)
다음은 슬라이더를 선택해서 똑같이, Bind to 는 Delegate로, 키 패스는 self.v 로 입력해준다.
앱 델리게이트의 프로퍼티와 텍스트레이블, 슬라이더를 바인딩으로 연결 완료했다. 재차 강조하지만 여기에는 프로퍼티 정의 외에는 어떠한 코드도 작성되지 않았다. 하지만 이것으로 앱은 완성이다.
이제 앱을 빌드하고 실행해보자. 화면에 나타나는 앱 윈도에서 슬라이더를 좌우로 움직여보자. 슬라이더를 움직이면 레이블의 숫자값이 쫘르르르 변하는 것을 볼 수 있다.
어떤 원리로 동작하나
코코아 바인딩은 과연 어떤 마법이길래, 이러한 뷰와 데이터 모델 간의 양방향 바인딩이 코드 한 줄 없이 자동으로 이루어질 수 있을까? 이는 기본적인 코코아의 기본 패턴/기능 몇 가지를 멋지게 조합한 결과물이다.
컨트롤의 특정한 프로퍼티와 컨트롤러 내의 모델 프로퍼티는 그 이름을 기준으로 연결된다.
컨트롤이 조작되면 바인딩이 연결된 객체와 키패스를 이용해서 해당 값을 업데이트한다.
반대로 컨트롤러 내의 어떤 값이 변경되면 그 값과 연결된 컨트롤들이 통지를 받고 값을 업데이트한다.
먼저 샘플 프로젝트에서 사용된 예를 살펴보자. 여기에는 NSLabel과 NSSlider 가 각각 하나씩 사용되었는데, 이들은 모두 NSControl의 자식 클래스들이다. 이쯤에서 NSControl에 대해서 간략히 살펴보자
NSControl
NSControl은 화면상에 표시되는 여러 컨트롤 디바이스를 표현하는 클래스이다. 표면적으로는 셀 이라는 것을 이용해서 시각적으로 표현되는데, 여기서는 그게 중요한 게 아니고 컨트롤은 어떤 값을 조정/조작하는데 사용되는 클래스라는 점이다. 실질적으로는 아무런 값을 가지지 않는 컨트롤(버튼 등)도 있고, 텍스트를 표현하는 레이블이나, 텍스트를 편집할 수도 있는 텍스트 필드, 슬라이더 등등 화면상에 표현되는 표준 UI 컴포넌트들은 모두 NSControl이라고 볼 수 있다.
NSControl은 어떤 값을 조작하는 UI 단위이다. 이 값은 기본적으로 objectValue 라고 하는 id 타입 객체이며, 하나의 컨트롤은 그 포맷에 따라 doubleValue, intValue, stringValue 등의 여러 타입의 프로퍼티를 가지고 있다. 하지만 이들은 모두 컨트롤 내부에서 추상클래스와 포매터를 기반으로 하나의 값의 다른 측면을 바라보는 것이다. 따라서 여러 프로퍼티에 혼란스러워하지 않고 컨트롤러 1개는 값 1개를 조작한다고 생각하면 된다.
NSControl은 코코아의 디자인패턴 중 target-action이 적용되는 전형적인 클래스이다. 타깃-액션은 컨트롤에 어떤 타깃이 주어지면, 사용자가 자신을 조작할 때 미리 정해진 타깃으로 액션 메시지를 전송하게 된다. 우리가 보통 인터페이스 빌더에서 컨트롤러 객체로 액션 메시지를 연결하는 행위는 해당 컨트롤이 트리거링되는 조작을 받았을 때, 어떤 컨트롤러에게 어떤 메시지를 보내야 하는지를 알려주는 것이라 해석할 수 있다.
키밸류 코딩
컨트롤이 조작되었을 때, 컨트롤을 타깃에게 어떠한 액션 메시지를 보내게 된다. 이 때 보통은 타깃이 뷰 컨트롤러이고, 코드상에서는 NSViewController 등의 클래스를 서브 클래싱하면서 IBAction 메소드를 정의하여 인터페이스 빌더에서 컨트롤과 뷰 컨트롤러간에 action 연결을 만들게 된다.
바인딩에서 액션은 임의의 커스텀 메소드를 사용하지 않았다. 대신에 이 액션은 하고자 하는 일이 명확하다. 바인딩하는 대상의 특정한 프로퍼티 키패스를 컨트롤러의 값으로 세팅하는 것이다. 우리는 샘플 프로젝트에서 AppDelegate에 v 라는 이름의 프로퍼티를 만들었다. AppDelegate는 NSObject의 서브 클래스이므로, 키밸류 코딩 컨벤션을 따른다.(이는 NSKeyValueCoding이라는 비정규 프로토콜에서 정의된다.) 따라서 바인딩되는 객체와 키 패스를 알고 있다면, 슬라이더는 그 자신이 조작되는 시점에 타깃에게 "self.v"라는 키패스의 값을 세팅하라는 메시지를 보낼 것이다.
이를 통해서 슬라이더를 움직이면 AppDelegate의 프로퍼티 v는 슬라이더의 값이 된다. 그러면 이 값이 변경되었을 때, 레이블은 어떻게 업데이트 되는 것일까?
키밸류 옵저빙
프로퍼티의 구조를 이해하고 있다면, 어떤 프로퍼티 값이 변경되었을 때 특정한 동작을 취하게 하는 것을 구현하는 방법에 대해서 감을 잡을 것이다. 보통은 setter 메소드를 정의하는 것이다. 샘플 프로젝트의 v 는 copy, nonatomic 시멘틱으로 정의되어 있다. 따라서 컴파일러는 다음과 같이 메소드를 합성해 낼 것이다.
문제는 이렇게하려면 앱 델리게이트가 해당 레이블에 대한 참조를 가져야한다는 것이다. 그런데 우리는 샘플 프로젝트에서 어떠한 아웃렛도 선언하거나 연결한 적이 없다. 그러면 어떻게 레이블은 그 값을 자동으로 변경할 수 있을까? 그 비밀은 키-밸류 옵저빙이다. 키 밸류 옵저빙은 어떤 객체가 다른 객체의 키패스에 대한 값이 변경될 때, 그것에 대한 통지를 받게 되는 매커니즘이다. 이와 관련된 내부적인 처리는 모두 런타임에서 수행되며, 우리는 그저 그 키패스에 해당하는 프로퍼티가 키밸류 코딩 규칙을 따르도록 정의하면 되는 것이다.2
바인딩을 세팅하게 되면, 앞서 살펴본바와 같이 타깃-액션 메시지를 보내는 기능을 동적으로 생성하는 동시에, UI컨트롤은 모델 키패스에 대하여 자신을 옵저버로 등록한다. 그리고 해당 프로퍼티가 변경되면, 그 프로퍼티의 값을 자신의 objectValue로 설정한다.
대략 다음과 같은식의 코드들이 자동으로 붙게 된다. 여기서 대상 객체를 target으로 했지만, 실제로 하나의 컨트롤은 여러 개의 모델과 바인딩될 수 있으므로 별도의 객체 식별자를 사용할 것으로 보인다.
...
// 아마도 awakeFromNib 쯤에...
[self.taget addObserver:self
forKeyPath:@"self.v"
options:NSKeyValueObservingNew
context:NULL];
...
// 그리고 바인딩된 모델 키패스의 변경을 감지하도록
- (void)observeValueForKeyPath:(NSString*)keypath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey, id>*)change
context:(void*)context
{
if([keypath isEqualToString:@"self.v") {
// 변경된 값을 자신의 값으로 치환한다.
(id)newVaue = [change objectForKey:NSKeyValueChangeNewKey];
self.objectValue = newValue;
} else {
[super observeValueForKeyPath:keypath
ofObject:object
change:change
context:context];
}
}
// 해제되기 직전에는 옵저버 등록을 해제해야 함
- (void)dealloc
{
[self.target removeObserverForKeyPath:@"self.v"];
}
슬라이더와 레이블 모두가 이런 식으로 특정한 모델 데이터에 바인딩 되어 있다. 샘플 프로젝트에서는 슬라이더를 통해서만 모델 값을 변경할 수 있었지만, + , – 버튼을 이용해서 값을 조정한다던가, 레이블 대신에 텍스트 필드를 사용해서 값을 입력받을 수 있게한다면 이 모든 관계들이 실시간으로 영향을 받으면서 변경되는 것이다.
Swift와 코코아 바인딩
키밸류코딩과 키밸류옵저빙은 모두 Objective-C 런타임에 전적으로 의존하는 매커니즘이다. 그러면 Swift로 작성하는 코코아 앱에서는 코코아바인딩을 사용할 수 없을까? 당연히 사용할 수 있다. 대신에 몇가지 조건이 있다.
모델 키패스를 포함하는 객체의 클래스는 NSObject의 서브 클래스여야 하며
Objective-C런타임에서 식별할 수 있게 @objc 접두어를 써서 정의해야 한다.
모델 키패스가 되려는 프로퍼티 역시 @objc 접두어를 써야 한다.
동시에 접근자 메소드를 동적으로 생성해내기 위해서 dynamic 키워드도 써야 한다.
따라서 샘플 프로젝트의 앱 델리게이트는 Swift로 작성됐다면 다음과 같은 모양이어야 할 것이다.
@objc
class AppDelegate: NSObject, NSApplicationDelegate
{
@IBOutlet weak var window: NSWindow!
@objc dynamic var v: float = 50.0;
}
그외에…
코코아 바인딩은 인터페이스 빌더에서 설정하는 것이 간편하다. 하지만 실제로 이 기능은 NSKeyValueBindingCreation이라는 비정규 프로토콜에 정의된 메소드를 호출하는 것으로 축약된다.
컨트롤러들은 (당연히도) 관리할 컨텐츠가 필요하며, 컨텐츠를 지정하는 방법에는 여러가지가 있다. 바인딩을 생성하는 코드를 통해서도 가능하며, IB를 통해서도 설정할 수 있다.
컨트롤러 컨텐츠 설정하기
NSObjectController와 그 서브클래스들은 -initWithContent:를 통해서 초기화되며, 만약 컨텐츠 바인딩을 의도한다면 nil을 넘겨도 된다. 혹은 명시적으로 setContent:를 통해서 지정해줄 수도 있다. 보통은 컨트롤러의 컨텐츠 바인딩을 통해서 연결하는 것이 일반적이다. 코코아바인딩 :: 컨트롤러의 컨텐츠 제공방법 더보기
코코아 바인딩은 뷰와 데이터 모델을 양방향으로 “묶어서(binding)” 한쪽에서의 변경이 다른쪽으로 자동으로 반영되게끔 하는 것이다. 예를 들어 슬라이더를 드래그하여 값을 변경한 것을 뷰 컨트롤러의 특정한 실수값 프로퍼티에 반영하도록 하거나, 혹은 클래스외부로부터 전달받은 메시지에 의해서 프로퍼티 값이 변경된 경우, 자동으로 이 값이 뷰에 반영되도록 하는 것이다.
이러한 기능은 코코아 바인딩을 쓰지 않고도 얼마든지 구현할 수 있지만, 코코아 바인딩을 사용하게 되면 많은 양의 “접합 코드”들을 작성하는 것을 생략할 수 있다. 예를 들어 다음과 같은 부분에 대한 코드를 일일이 작성해야 한다.
데이터의 변경을 업데이트해 줄 뷰를 참조하기 위한 IBOutlet 선언 및 연결
데이터의 변경에 맞추어 실제 뷰 속성을 변경하기 위한 setter 메소드 작성
뷰상의 변경이 발생할 때 데이터를 변경해주기 위한 IBAction 메소드 선언 및 작성 (그리고 연결)
코코아 바인딩은 이러한 코드 없이 뷰와 데이터 모델이 동기화될 수 있도록 한다. 이 기술은 몇 가지 Objective-C 및 코코아의 기본 프레임 기술에 기반하고 있다. 고정된 액세스 메소드가 아닌 키 이름으로 객체의 프로퍼티를 접근하는 키-밸류 코딩(KVC), 다른 객체의 특정 키패스의 값이 변경되는 것을 알아차리기 위한 키-밸류 옵저빙(KVO) 그리고 한 객체의 특정 값이 다른 객체의 프로퍼티에 묶이도록 하는 키밸류 바인딩(KVB)이 그것이다.
또한 컨트롤의 변경과 값 업데이트 시점을 맞추기 위해서는 NSEditor와 NSEditorRegistration 프로토콜이 사용되기도 한다.
바인딩의 양상
앞서 바인딩은 간단하게 특정한 UI 컨트롤과 모델 데이터를 묶는 것이라 이야기했다. 실질적으로 바인딩은 두 객체 사이의 프로퍼티가 연결되어 양방향으로 동기화되는 것을 의미한다. 슬라이더 컨트롤과 다른 어떤 객체의 number라는 프로퍼티에 대해서 생각해보자.
[NSSlider] - value ------> Bind to: ModelObject ---> @"self.number"
- minValue
- maxVale
- enabled
...
실제로는 NSSlider의 많은 프로퍼티 중에 value가 (이 value 속성은 objectValue, intValue, doubleValue, stringValue… 의 통칭이라 하겠다.) 다른 객체인 ModelObject 타입의 객체의 number라는 프로퍼티와 바인딩이 된다고 볼 수 있다.
ModelObject 클래스에서 number 프로퍼티를 키밸류 코딩 호환의 방식1으로 구현했다면 이 프로퍼티는 실제로 접근자 메소드를 사용하지 않아도 동적으로 키패스를 통해서 액세스가 가능해진다. 따라서 키패스를 알려주어 슬라이더가 데이터 값을 변경할 수 있게 하고, 또 동시에 키-밸류 옵저빙을 통해서 해당 키패스의 변경을 감지하여 그 값으로 자신의 상태를 업데이트할 수 있게 하는 것이 바인딩 작용의 가장 기본적인 연결이다.
이러한 작용들은 바인딩이 만들어진 후에 내부적으로 처리된다. 바인딩은 주로 인터페이스 빌더에서 설정된다. 물론 추천하는 방법은 아니지만, 바인딩의 생성은 코드상으로 처리할 수도 있다. AppKit에는 바인딩을 만들기 위한 함수가 정의되어 있다.
포매터는 특정한 타입의 데이터를 다시, 특정한 형식의 문자열로 변환하여 표현할 때 사용하는 클래스들이다. Cocoa에는 여러 타입의 포매터 클래스가 정의되어 있고, 원한다면 필요한 포매터를 직접 디자인하여 사용할수도 있다. UI상으로 어떤 값을 표현할 때, 슬라이더와 같이 그 값을 그래픽으로 표현하는 컨트롤도 있지만, 레이블이나 텍스트 필드와 같이 문자열값을 사용하는 경우도 있다. 하지만 모델 키패스에 정의된 프로퍼티의 타입은 문자열 타입이 아닌 경우도 있기 때문에 NSNumberFormatter나 NSDateFormatter와 같은 포매터를 사용해서 해당 값을 적절한 형식의 문자열로 변환한다. 포매터는 바인딩 체인 내에 포함되는 것은 아니나, 코코아 바인딩을 사용하는 앱에서 중요한 위치를 차지한다. 그것은 포매터가 값 –> 문자열의 단방향 변환이 아닌 양방향 변환을 지원하기 때문이다.
텍스트 필드를 NSNumber 타입의 프로퍼티와 바인딩하는 경우를 생각해보자. 프로퍼티가 변경되었을 때, 해당 값을 표현하는 컨트롤러입장에서는 NSNumber 값이 변경되면 그 값을 setObjectValue:로 받아서 셀이 렌더링할 수 있게끔한다.
반대로 이 텍스트 필드에 값을 입력하여 숫자값을 변경하려한다면, 텍스트 필드의 문자열 값을 정수나 실수 타입의 값으로 해석해야 하는 과정이 필요하다. 포매터들은 이렇게 컨트롤의 값을 문자열로 표현하거나, 반대로 문자열로 만들어진 값을 특정 타입의 값으로 변환할 수 있다. 포매터는 코코아바인딩에서는 필수적인 요소는 아니지만 액션메소드에서 텍스트 정보를 다른 형식으로 변환해야 하는 경우를 제거해줄 수 있다.
[NSTextField] - value --> bind to: MyDataModel --> keyPath: @"number"
|
NSTextFieldCell :: 값이 텍스트로 입력되면, 포매터를 통해 숫자로 변환하여 바인딩에 태운다.
|
NSNumberFormatter
값 트랜스포머
값 변환기(?)라고 번역하면 조금 이상한 것이, 포매터 역시 변환기의 일종이기 때문이다. 트랜스포머와 포매터가 다른 점은 포매터는 컨트롤의 셀에 표시될 값과 실제 데이터값 사이의 변환을 담당한다. (예를 들어 NSDate 값을 “12월 12일”인 문자열로 변환하거나 그 역의 변환을 수행) 대신 트랜스포머는 모델에서 사용하는 값과 뷰에서 사용하는 값 사이를 변환한다. 예를 들어 실제 모델에서는 도(dgree)단위의 각도를 사용하지만, 뷰에서는 라디안을 써야 한다거나, 모델 데이터는 섭씨 온도이나, 뷰에서는 화씨로 표현할 때 사용할 수 있다. (이 말은 섭씨-화씨 트랜스포머를 구현했다면, 이 두 온도의 변환기를 코코아 바인딩으로 손쉽게 만들 수 있다는 이야기이다.)
트랜스포머는 바인딩되는 두 개의 객체 사이에서 미들맨의 역할을 하면서 두 사이를 오가는 값을 변환한다. 대신, 트랜스포머를 사용하는 경우라도 특정 프로퍼티를 트랜스포머에 연결하는 것은 아니며, 해당 바인딩이 사용해야하는 트랜스포머 이름을 지정하는 식으로 등록한다.
참고로, 트랜스포머를 등록하기 위해서는 NSValueTransformer 클래스에 이름과 그에 해당하는 트랜스포머 인스턴스를 등록해야 한다. 그런데 인터페이스 빌더에서 지정한 정보들이 해석되는 Nib 로딩 시점에 바인딩이 구성될 것이므로, 이 지점에서는 사용하려는 트랜스포머가 등록되어 있어야 한다. 따라서 만약 커스텀 트랜스포머를 사용하려는 경우에는 앱 론칭 프로세스의 극 초반 과정인 앱 델리게이트의 +initialize 메소드에서 시작해야 한다.
오브젝트 컨트롤러
오브젝트 컨트롤러는 NSObjectController 혹은 NSArrayController의 인스턴스로, UI컨트롤과 뷰 사이에서 바인딩을 중개하는 역할을 한다. 단순한 양방향 바인딩에서 오브젝트 컨트롤러는 필요없기는 하지만, 그 자식 클래스인 NSArrayController는 몇 가지 사용해야 할 장점이 있다.
화면에 테이블 뷰가 하나 있다고 생각해보자. 모든 셀에 대해서 일일이 바인딩을 만들기는 어려울 뿐더러, 테이블 뷰의 콘텐츠는 동적으로 액세스되므로 N:N으로 바인딩할 수 있는 옵션이 필요할 것이다. NSArrayController는 코드를 사용하지 않고 바인딩만으로 테이블 뷰를 구성할 수 있게 할 수 있다. 뿐만 아니라 배열 그 자체가 그대로 바인딩 되는 경우, 새로운 요소가 추가되거나, 기존 요소가 삭제 혹은 이동하는 UI 상의 변경이 일어나게 되면 코코아 바인딩은 새로운 배열을 생성해서 기존 배열을 교체하는 식으로 동작하게 된다. 이 때 NSArrayController를 사용하면 개별 원소 단위에서 콘텐츠를 컨트롤하기 때문에 성능 측면에서도 유리하다.
다음은 배열 컨트롤러를 사용해서 테이블 뷰의 콘텐츠를 테이블뷰 델리게이트/데이터소스 메소드 구현 없이 코코아 바인딩만으로 populating 하는 예를 보여준다. (뷰 기반 테이블 뷰)
배열 컨트롤러는 일련의 객체 집합을 다루는 바인딩 호환 객체이다. 여기서 ‘집합’은 주로 NSArray인데, 코어데이터 등에서는 NSSet을 쓰기도 한다. 배열 컨트롤러는 배열이 아니고 별도의 배열을 관리한다. 이는 배열을 자동으로 재정렬하거나, 선택된 객체들을 기억하거나 하는 배열 자체에 저장될 수 없는 몇 가지 메타 정보를 가지고 배열을 다루는 것을 도와준다.
배열 컨트롤러는 NSObjectController의 서브클래스로, 다음 메소드를 통해서 초기화할 수 있다.
- (instancetype)initWithContent:(id)content
다시 NSObjectController는 NSController의 자손인데, 이 컨트롤러는 객체의 편집 과정에 따른 이벤트를 발생시키는 기능을 갖고 있다.