# Delegation
Delegation은 프로그램의 한 객체가 다른 객체를 대신하거나 다른 객체와 함께 작동하는 간단하고 강력한 디자인 패턴이다. Delegation 객체는 다른 객체(delegate)에 대한 참조를 유지하고 적절한 타이밍에 메시지를 보낸다. 이 메시지는 delegation 객체가 처리하려고 하거나 방금 처리한 이벤트의 delegate에게 보내진다. delegate는 자체 또는 어플리케이션의 다른 객체의 모양이나 상태를 업데이트하여 메시지에 응답할 수 있으며 경우에 따라 발생할 이벤트를 처리하는 방법에 영향을 주는 값을 반환할 수 있다. delegation의 주요 가치는 하나의 중심 객체에서 여러 객체의 동작을 쉽게 customize 할 수 있다는 것이다.
## Delegation과 Cocoa Frameworks
Delegating 객체는 일반적으로 프레임워크 객체이고 delegate 객체는 일반적으로 custom controller 객체이다. 관리되는 메모리 환경에서 delegating 객체는 delegate에 대한 weak reference를 유지한다. garbage-collected 환경에서 receiver는 delegate에 대한 strong reference를 유지한다. Delegation의 예는 ```Foundation```, ```UIKit```, ```AppKit``` 및 기타 Cocoa 및 Cocoa Touch 프레임워크에 많이 있다.
Delegating 객체의 예시는 AppKit 프레임워크의 ```NSWindow```클래스 인스턴스이다. NSWindow는 ```windowShouldClose:```메서드를 포함하는 프로토콜을 선언한다. User가 window에서 닫기를 클릭하면 window객체는 ```windowShouldClose:```를 delegate에게 보내 창 닫기를 확인하도록 요청한다. delegate는 Boolean 값을 반환하므로 window 객체의 동작을 제어하게 된다.

<br/>
## Delegation and Notifications
대부분의 Cocoa 프레임워크 클래스의 delegate는 delegating 객체가 post한 notification의 observer로 자동 등록된다. delegate는 특정 notification 메시지를 수신하기 위해 프레임워크 클래스에서 선언한 notification 메서드만 구현하면 된다. 위에서 언급한 예제에 따라 window 객체는 ```NSWindowWillCloseNotification```을 observer에게 post하지만 ```windowShouldClose:``` 메시지는 window 객체의 delegate에게 보낸다.
## Data Source
데이터 소스는 delegate와 거의 동일하다. 차이점은 delegating 객체와의 관계에 있다. 데이터 소스는 사용자 인터페이스 제어를 위임받는 대신에 데이터 제어를 위임받는다. 일반적으로 테이블뷰와 같은 뷰 객체인 delegating 객체는 데이터 소스에 대한 참조를 소유하고 때때로 화면에 표시할 데이터를 요청한다. delegate와 비슷한 데이터 소스는 프로토콜을 채택하고 최소한 해당 프로토콜의 메서드 필수 조건을 구현해야 한다. 데이터 소스는 delegating view에 제공하는 model 객체의 메모리 관리를 담당한다.
---
# Using Delegates to Customize Object Behavior
delegator를 대신하여 이벤트에 응답하기
## 개요
Cocoa 객체와 앱에서 일어난 이벤트를 알리는 상호작용을 위해 delegate를 사용하자.
## Delegate Protocol 적용하기
코코아 API는 보통 delegate 메서드를 포함하는 프로토콜을 제공한다. User가 window의 사이즈를 조절하는 등의 이벤트가 발생했을 때, delegator 클래스는 이벤트를 감지하고 delegate로 지정한 객체의 메서드를 호출할 것이다. delegate 메서드는 이벤트에 어떻게 반응할지에 대해 customize 할 수 있다.
[예제] NSWindowDelegate 프로토콜을 채택한 Delegate 클래스 정의
```swift
class MyDelegate: NSObject, NSWindowDelegate {
func window(_ window: NSWindow, willUseFullScreenContentSize proposedSize: NSSize) -> NSSize {
return proposedSize
}
}
```
## delegates가 존재하는지 확인하기
코코아 delegation 패턴은 delegate 객체의 인스턴스화를 요구하지 않는다. 이벤트에 응답할 필요가 없다면 delegate를 만들 필요가 없다. 어떤 객체의 delegate에 있는 메서드를 호출하기 전에 해당 delegate가 ```nil```이 아닌지 확인해야한다.
[예제] 옵셔널 체이닝을 사용해 window의 delegate가 존재하는지 확인
```swift
let myWindow = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 5120, height: 2880),
styleMask: .fullScreen,
backing: .buffered,
defer: false
)
myWindow.delegate = MyDelegate()
if let fullScreenSize = myWindow.delegate?.window(myWindow, willUseFullScreenContentSize: mySize) {
print(NSStringFromSize(fullScreenSize))
}
```
위 예제에서 myWindow에 delegate라는 프로퍼티가 없음에도 ```myWindow.delegate```에 접근하여 인스턴스를 만들어 주는 것이 이해가 잘 되지 않아 ```NSWindow```클래스를 찾아보았다.
해당 클래스에는 인스턴스 프로퍼티로 ```var delegate```가 정의되어 있는 것을 확인할 수 있었다. 결과적으로 myWindow는 NSWindow를 상속함에 따라 별도로 프로퍼티를 정의하지 않았지만 delegate 프로퍼티를 상속받았다는 것을 알 수 있었다.
delegate 프로퍼티의 정의를 확인하면 다음과 같다.
```swift
weak var delegate: NSWindowDelegate? { get set }
```
인스턴스 프로퍼티가 NSWindowDelegate 프로토콜을 준수하는 타입만을 가지는 것은 이해가 됐으나 뒤에 { get set }은 왜 붙어있는지 모르겠다. 활동학습 시간에 질문해 보도록 하자.
정리하면, Cocoa API 중에는 따로 구현하지 않아도 delegate 프로퍼티와 그에 따른 프로토콜이 정의되어 있으므로 필요할 때 delegate 인스턴스를 생성하여 할당하면 손쉽게 적용이 가능하다는 내용으로 보인다.