Delegation Pattern
글을 읽기전에 먼저 숙지하고 계셔야할 주제들입니다.
someObjectProperty.delegate = self
많이 보셨던 모양이실겁니다.
프로토콜을 사용할 때, 특히 저는 TableView를 다룰때 가장 많이 접했던 것으로 기억이 됩니다. 이것이 무엇을 의미할까요?
Delegation Pattern을 구글링해보시면 해석을 통해 시작하는 경우가 많습니다.
그 뜻은 직역하자면 위임하다
등의 의미로 해석이 됩니다.
쉽게 표현하자면 어떤 객체에서 일어나는 이벤트에 관한 혹은 어떤 객체에 뿌려줄 데이터에 관한 코드를 다른 객체에서 작성해주는 것을 말합니다. 즉 A객체의 일을 B객체에서 대신해주는 일을 위임하는 행위입니다.
여기서 A객체는 Sender, B객체는 Receiver로 많이 표현됩니다.
그리고 이러한 행위는 반드시 protocol
을 동반합니다.
이들의 관계를 정리해보자면 다음과 같습니다.
- protocol : 해야할 일의 목록
- sender : 일을 시키는 객체
- receiver : 일을 하는 객체
bobthedeveloper
의 bob님은 이를 사장과 비서로 비유하여 설명해주셨습니다.
bobthedeveloper 블로그
개인적으로 bobthedeveloper 님의 블로그가 Swift를 공부하는데 정말 큰 도움이 되었습니다. 저는 Delegation Pattern에 관해서는 좀 더 알아보고 싶어서 검색하던 도중 좋은 예제가 있어서 이렇게 글을 작성하게 되었습니다.
구현 방법과 예제를 통해 Delegation Pattern에 대해 공부를 시작해보도록 하겠습니다.
먼저 알아야 할 것은 Delegation Pattern은 ios에서만 사용되는 것이 아닙니다. 하지만 ios에서 사용되는 경우가 가장 흔하기도 합니다. 위에서 언급했듯이 일을 시키고 일을 함을으로써 객체들간의 대화를 하는 주된 방법중 하나가 Delegation Pattern입니다.
ios에서 이러한 객체들간의 대화를 하는 방법에는 여러가지 방법이 있습니다.
- NotificationCenter
- KVO(Key-Value Observing)
- Completion handlers/Callbacks (using closures)
- Target-Action
해당 방법들에 대해서는 다루지 않도록 하겠습니다. 위의 링크를 타고 들어가면 자세히 설명되어 있습니다.
어떻게 Delegation을 적용해야 하나?
간단하게 정리하자면 다음과 같습니다.
요구사항을 파악한다.
각각의 Delegate들은 Delegate Protocol에 의해 나열된 그들의 규칙이 있습니다. 이 규칙은 위의 표에서 언급한 일의 목록과 같습니다. 이들은 함수 시그니쳐들의 집합으로 해당 protocol을 따르게 된다면 반드시 구현해야하는 함수들입니다.
protocol SomeDelegate { func someTask() }
Delegation을 따른다/채택한다.
- 클래스 뒤에
: SomeDelegate
를 작성함으로써 해당 클래스가SomeDelegate
protocol을 따른다는 것을 표현할 수 있습니다.
- 클래스 뒤에
Delegate 객체(일을 시키는 객체)와 연결을 한다.
- protocol을 따르는 객체는 일의 목록을 수행해야하는데 ( 함수를 구현 ), 그 전에 이 일이 어떤 객체로부터 주어졌는지를 명시해주어야 합니다.
요구사항들 구현하기
- 우리의 비유법으로 표현하자면 요구한 일을 수행하기, 즉 protocol의 함수들을 구현하기 단계입니다.
예제
단순히 위의 구현법만 본다면 감이 오시지 않으실 수도 있습니다. 그럼 직접 예제 코드를 보며 위의 방법들을 다시 하나하나 살펴보도록 하는 시간을 갖도록 하겠습니다.
오디오 파일 재생과 관련된 어플리케이션을 제작한다고 할 때. 몇몇의
viewController
들은audio player
의 뷰를 갖고 있을 것입니다. 해당 뷰가 정확히 어떤 모습일지는 모르지만 플레이버튼, 정지버튼 그리고 플레이 리스트를 보여주는 버튼이 있을 것입니다.
audio player
뷰는 그만의 UIView
클래스 파일과 .xib
파일로 분리되어 있습니다. 그리고 그 뷰는 서브뷰로써 viewController
에 붙을 것입니다.
이제 해당 viewController
에 붙어 있는 해당 뷰의 버튼들에 어떻게 하면 버튼의 기능을 추가할 수 있을까요?
간단하게 생각하면 audio player
뷰 클래스 파일에 IBAction
을 추가하는 방법이 있을 수 있습니다. 겉보기에는 괜찮은 방법일지 모르나 조금만 더 생각해보면 이는 옳지 않은 방법이라는 것을 아실수 있습니다.
만일 viewControllerA
와 viewControllerB
위에 각각 audio player
뷰가 존재하면서 viewControllerA
에서 audio player
뷰의 플레이 리스트 버튼을 누르면 리스트가 tableView
형태로, viewControllerB
에서는 pickerView
형태로 나타나게 하고 싶다면 이를 어떻게 구현해야 할까요?
Audio Player View :
/*
#은 위의 구현방법의 나열된 순서를 나타냅니다.
*/
// #1. 요구사항에 해당하는 protocol 디자인
protocol AudioPlayerDelegate {
func playPauseDidTap()
func playlistDidTap()
}
class AudioPlayerView: UIView {
@IBOutlet weak private var btnPlayPause: UIButton!
@IBOutlet weak private var btnPlayList: UIButton!
var delegate: AudioPlayerDelegate?
@IBAction private func playPauseTapped(_ sender: AnyObject){
delegate?.playPauseDidTap()
}
@IBAction private func playlistTapped(_ sender: AnyObject){
delegate?.playlistDidTap()
}
}
이는 audio player
뷰의 클래스이며 delegate를 통해 버튼 이벤트가 발생하였을 때 처리해야할 일의 목록들을 채택한 클래스를 지정합니다.
AudioPlayerDelegate
가 어떻게 클래스를 참조할 수 있는지 이해가 되시지 않는다면"Protocol as Type"
이란 키워드로 검색을 해보시길 바랍니다.
View Controller :
// #2. 해당 Delegation Protocol을 채택
class ViewController: UIViewController, AudioPlayerDelegate {
var audioPlayer: AudioPlayerView?
override func viewDidLoad(){
super.viewDidLoad()
audioPlayer = AudioPlayerView()
// #3. ViewController가 AudioPlayerView에서 일어난 이벤트의 관련된 일을 할 것이라고 명시
audioPlayer?.delegate = self
}
// #4. 채택한 일의 목록(protocol)의 일들을 시행(구현)
func playerPauseDidTap(){
print("play/pause tapped!")
}
func playlistDidTap(){
print("list tapped!")
}
}
일에 대한 처리를 구현한 함수들의 내용은 위에서 언급한
viewControllerA
와viewControllerB
의 예시처럼 달라질 수 있습니다.
viewController
을 다음과 같이 작성한다면 해당 viewController
위에 있는 audio player
뷰에서 이벤트가 발생한다면 구현해놓은 함수들의 코드가 실행됩니다.
마무리
이렇게 Delegation Pattern에 관한 글을 정리해보았습니다.
물론 이 글만 본다고 Delegation Pattern에 관해서 100프로 이해하실 수는 없을 수도 있습니다. 하지만 조금이나마 저의 글이 도움이 되었길 바라겠습니다. 또한 제가 검색해보면서 읽어보면서 도움이 되었던 페이지들을 하단에 남겨놓겠습니다.
감사합니다.