디자인 패턴

Delegate pattern - iOS

호종이 2022. 7. 23. 14:56

Delegate Pattern 이란?

Delegate라는 단어는 `위임하다` 라는 뜻을 지니고 있다. 즉 Delegate Pattern 이란 주로 객체의 행동(메소드) 또는 프로퍼티 의 구현을 다른 객체에게 맡겨야 할 필요가 있을 때 사용한다. 아래의 다이어그램을 살펴보자

 

 

  • Object using a delegate - delegate 가 필요하거나 사용하는 객체로, 어떤 이벤트가 발생했을 때 이에 대응되는 delegate 의 메소드를 호출한다.
  • Delegate protocol - delegate object 가 구현해야 할 메소드들을 정의하는 protocol 이다.
  • Object acting as a delegate - delegate protocol 을 준수하는 객체로, Object using a delegate 가 사용하는 Concrete type 이 된다.

Delegate Pattern 의 사용처

iOS framework 에서 delegate 라는 말을 심심치 않게 볼 수 있다. UITableViewDelegate, SceneDelegate, AppDelegate 등, Application 의 Life cycle 에서 이벤트를 호출할 때 실행시킬 delegate 메소드를 protocol 로 선언하고, 구현은 우리 개발자들에게 맡김으로써 좀 더 유연하게 application 을 개발할 수 있다.

 

아마 TableView 혹은 CollectionView 를 구현할 때 Delegate pattern 을 가장 자주 접하게 될 것이다. TableView 를 구현하는 예제를 아래에서 살펴보도록 하자.

 

UIKit framework 에서 ViewController 가 View 들을 가지고 있고, 따라서 TableView 또한 ViewController 위에 위치하고 있다.

따라서 이것을 코드로 표현하면 다음과 같다.

 

import UIKit

final class TableViewController: UIViewController {

    // ViewController 가 View 를 가지고 있다.
    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

 

여기서 추가적으로 tableView 에 나타낼 데이터들을 제공하는 UITableViewDataSource 와, tableView 의 row 를 클릭하거나, tableView 의 셀이 나타났을 때 개발자가 정의한 행동을 하도록 구현하고 싶으면 UITableViewDelegate 를 구현해 tableView 의 delegate 로 설정해주어야 한다.

 

final class TableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    // ViewController 가 View 를 가지고 있다.
    @IBOutlet weak var tableView: UITableView! {
        didSet {
            tableView.delegate = self
            tableView.delegate = self
        }
    }

 

ViewController 가 UITableViewDelegate 와 UITableViewDataSource 를 준수함으로써, 이제 TableViewController 는 TableView 와 관련된 작업을 수행하는 책임을 가지게 된다. 즉 ViewController 가 tableView 에게 보여줄 데이터도 제공해야 하고, tableView 가 보여줄 Cell 의 UI 도 제공해야 하고, tableView 의 row 가 선택되었을 때 어떤 행동을 할 지 정의할 수 있다.

 

지금까지의 상태를 다이어그램으로 보면 다음과 같을 것이다.

 

 

Retain cycle 을 조심하자

위 다이어그램을 다시 한 번 보자. TableView 와 ViewController 간의 순환 사이클이 존재하는 것을 알아차렸을 것이다. 참조 타입간의 순환 참조가 일어나면 ARC 의 메모리 관리 규칙에 의해 메모리 누수가 일어날 수 있고, 이런 순환 참조를 최대한 피하라는 것을 우리는 이미 알고 있다. 만약 이런 상황이 일어나면 weak, unowned 같이 Reference count 를 증가시키지 않는 방향으로 객체 참조를 하는 해결 방법도 알고 있다.

 

TableView Delegate 의 선언부분을 살펴보자

@MainActor open class UITableView : UIScrollView, NSCoding, UIDataSourceTranslating {
	...
    
    weak open var dataSource: UITableViewDataSource?
    weak open var delegate: UITableViewDelegate?
    ...
}

 

TableView 는 이런 식의 순환 참조를 피하기 위해 delegate 를 weak 참조하는 것을 알 수 있다.

 

즉 delegate 패턴을 사용해서 문제를 해결하려 할 때 보통 delegate 를 weak 로 소유해서 순환 참조로 인한 메모리 누수를 방지하는 것이 일반적이다.