Lộ trình học Lập trình viên iOS 2025 là một hành trình dài với vô vàn khái niệm và mẫu thiết kế (Design Patterns) cần nắm vững. Trong số đó, Delegate Pattern nổi bật lên như một kỹ thuật truyền thông mạnh mẽ và linh hoạt, là trái tim của rất nhiều framework cốt lõi trong iOS như UIKit hay Foundation. Hiểu và sử dụng thành thạo Delegation không chỉ giúp bạn viết code sạch hơn, dễ bảo trì hơn mà còn mở ra cánh cửa để làm việc hiệu quả với các component có sẵn của Apple.
Mục lục
Delegate Pattern là gì?
Delegate Pattern (Mẫu ủy quyền) là một trong những Design Pattern hướng đối tượng phổ biến nhất trong phát triển ứng dụng iOS và macOS. Về cơ bản, nó cho phép một đối tượng (gọi là delegating object hoặc source object) giao một số trách nhiệm hoặc nhiệm vụ của mình cho một đối tượng khác (gọi là delegate object). Thay vì tự mình thực hiện một công việc nào đó, đối tượng nguồn sẽ “ủy quyền” cho đối tượng được ủy quyền thực hiện công việc đó và thông báo lại kết quả (thường thông qua các phương thức của protocol).
Pattern này tạo ra mối quan hệ “một đối một” giữa đối tượng nguồn và đối tượng được ủy quyền. Đối tượng nguồn cần một cách để thông báo cho đối tượng được ủy quyền về các sự kiện xảy ra hoặc yêu cầu đối tượng được ủy quyền cung cấp thông tin hoặc thực hiện hành động. Swift Protocol chính là công cụ lý tưởng để định nghĩa “hợp đồng” này.
Tại sao Delegate Pattern lại quan trọng trong iOS?
Hệ sinh thái Cocoa và Cocoa Touch (nền tảng của iOS) sử dụng Delegate Pattern rộng rãi. Bạn sẽ gặp nó ở khắp mọi nơi, từ cách UITableView
tương tác với dữ liệu và giao diện hiển thị, cách UITextField
xử lý đầu vào của người dùng, cho đến cách các đối tượng mạng xử lý phản hồi. Việc Apple áp dụng mạnh mẽ mẫu này chứng tỏ sự hiệu quả của nó trong việc:
- Tách rời trách nhiệm (Separation of Concerns): Đối tượng nguồn chỉ tập trung vào nhiệm vụ chính của nó, còn các hành vi hoặc dữ liệu phụ trợ được giao cho đối tượng khác xử lý. Điều này giúp code gọn gàng, dễ hiểu và dễ kiểm thử hơn. Đây là một khía cạnh quan trọng của Lập trình hướng đối tượng mà chúng ta đã thảo luận trong bài viết OOP và Lập trình Hàm trong iOS: Chọn Lựa Nào Cho Từng Tình Huống?.
- Tái sử dụng (Reusability): Các component như
UITableView
được thiết kế chung chung để có thể hiển thị bất kỳ loại dữ liệu nào. Nó không cần biết dữ liệu đó là gì hoặc hiển thị như thế nào. Thay vào đó, nó ủy quyền việc cung cấp dữ liệu và cấu hình hiển thị cho một đối tượng delegate (thường là ViewController hoặc một class riêng biệt). Điều này cho phép mộtUITableView
có thể được tái sử dụng trong nhiều ngữ cảnh khác nhau chỉ bằng cách cung cấp một delegate khác. - Linh hoạt (Flexibility): Đối tượng nguồn không cần biết cụ thể đối tượng được ủy quyền là class nào, chỉ cần biết rằng nó tuân thủ một protocol nhất định. Điều này cho phép bạn dễ dàng thay đổi hành vi của đối tượng nguồn bằng cách thay đổi đối tượng được ủy quyền mà không cần sửa đổi code của đối tượng nguồn.
- Trao đổi dữ liệu và sự kiện hai chiều: Delegation cho phép đối tượng nguồn thông báo các sự kiện (ví dụ: người dùng chạm vào một hàng trong TableView) và đối tượng được ủy quyền có thể phản hồi lại (ví dụ: chuyển sang màn hình chi tiết). Ngược lại, đối tượng được ủy quyền cũng có thể cung cấp dữ liệu hoặc quyết định hành vi cho đối tượng nguồn (ví dụ: số lượng hàng cần hiển thị trong TableView).
Nắm vững Delegate Pattern là bước đệm quan trọng để bạn hiểu sâu hơn về cách hoạt động của UIKit và các framework khác của Apple, và là nền tảng cho việc xây dựng giao diện người dùng cơ bản mà chúng ta đã bắt đầu tìm hiểu trong các bài viết như Xây dựng Giao diện Người dùng Cơ bản với UIKit: Views, Controllers và Storyboards.
Các thành phần chính của Delegate Pattern
Delegate Pattern bao gồm ba thành phần chính:
- Protocol (Giao thức): Định nghĩa “hợp đồng” về các phương thức (và có thể là thuộc tính) mà đối tượng được ủy quyền phải (hoặc có thể) triển khai. Protocol chỉ định nghĩa giao diện, không phải logic triển khai.
- Delegating Object (Đối tượng nguồn): Là đối tượng sở hữu một delegate và gọi các phương thức trên delegate đó khi các sự kiện xảy ra hoặc cần thông tin. Đối tượng này thường có một thuộc tính optional kiểu Protocol để giữ tham chiếu đến delegate.
- Delegate Object (Đối tượng được ủy quyền): Là đối tượng tuân thủ Protocol được định nghĩa và cung cấp logic triển khai cho các phương thức trong Protocol. Thường thì đây là một ViewController hoặc một class khác chịu trách nhiệm xử lý các sự kiện hoặc cung cấp dữ liệu cho đối tượng nguồn.
Chúng ta có thể tóm tắt vai trò của từng thành phần trong bảng sau:
Thành phần | Vai trò | Kiểu trong Swift |
---|---|---|
Protocol | Định nghĩa các phương thức mà delegate cần (hoặc có thể) triển khai. | protocol |
Delegating Object | Đối tượng gốc. Chứa một tham chiếu đến delegate và gọi các phương thức của delegate. | class hoặc struct |
Delegate Object | Đối tượng được ủy quyền. Tuân thủ protocol và triển khai các phương thức của delegate. | class hoặc struct (thường là class khi cần sử dụng weak ) |
Triển khai Delegate Pattern trong Swift
Hãy cùng xem một ví dụ đơn giản về cách triển khai Delegate Pattern bằng Swift.
Bước 1: Định nghĩa Protocol
Đầu tiên, chúng ta định nghĩa protocol để mô tả các phương thức mà delegate cần triển khai. Giả sử chúng ta có một custom View tên là MyCustomInputView
và nó cần thông báo cho ViewController biết khi người dùng nhập xong text.
import UIKit
protocol MyCustomInputViewDelegate: AnyObject {
// Phương thức này được gọi khi người dùng nhập xong text
func myCustomInputViewDidFinishEditing(_ inputView: MyCustomInputView, withText text: String)
// Phương thức tùy chọn, được gọi khi text thay đổi
func myCustomInputViewTextDidChange(_ inputView: MyCustomInputView, newText text: String)
}
Ở đây, chúng ta sử dụng : AnyObject
sau tên protocol. Điều này giới hạn protocol chỉ có thể được áp dụng bởi các kiểu tham chiếu (class). Đây là một thực hành tốt trong Delegate Pattern để cho phép thuộc tính delegate trong đối tượng nguồn có thể được khai báo là weak
, từ đó tránh được Strong Reference Cycles (vòng lặp tham chiếu mạnh) mà chúng ta đã tìm hiểu trong bài viết về Quản Lý Bộ Nhớ trong Swift: ARC, Tham Chiếu và Các Thực Hành Tốt Nhất.
Bạn có thể đánh dấu các phương thức trong protocol là optional
nếu delegate không bắt buộc phải triển khai chúng. Tuy nhiên, trong Swift hiện đại, cách phổ biến hơn là sử dụng phương thức mặc định (default implementation) trong phần mở rộng (extension) của protocol hoặc đơn giản là chỉ định nghĩa các phương thức bắt buộc.
Bước 2: Tạo Delegating Object
Tiếp theo, chúng ta tạo class MyCustomInputView
. Class này sẽ có một thuộc tính kiểu MyCustomInputViewDelegate
để giữ tham chiếu đến delegate của nó.
import UIKit
class MyCustomInputView: UIView {
// Thuộc tính delegate, khai báo là weak để tránh giữ tham chiếu mạnh
weak var delegate: MyCustomInputViewDelegate?
private let textField: UITextField = {
let tf = UITextField()
tf.borderStyle = .roundedRect
tf.placeholder = "Nhập gì đó..."
return tf
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
private func setupView() {
addSubview(textField)
textField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
textField.topAnchor.constraint(equalTo: topAnchor, constant: 8),
textField.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
textField.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8),
textField.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8)
])
// Gán delegate cho UITextField để nhận thông báo
textField.delegate = self // MyCustomInputView sẽ là delegate của UITextField
// Thêm target cho sự kiện text thay đổi (tùy chọn)
textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
}
// MARK: - UITextFieldDelegate Methods (Delegating Object is delegate of UITextField)
// Phương thức này thuộc UITextFieldDelegate, MyCustomInputView triển khai để nhận thông báo từ UITextField
func textFieldDidEndEditing(_ textField: UITextField) {
// Khi UITextField kết thúc chỉnh sửa, MyCustomInputView thông báo cho delegate của chính nó
if let text = textField.text, !text.isEmpty {
delegate?.myCustomInputViewDidFinishEditing(self, withText: text)
}
}
// MARK: - Custom Target Methods (MyCustomInputView informs its delegate)
@objc private func textFieldDidChange(_ sender: UITextField) {
if let text = sender.text {
// Thông báo cho delegate của MyCustomInputView khi text thay đổi (nếu delegate triển khai phương thức này)
delegate?.myCustomInputViewTextDidChange(self, newText: text)
}
}
// Cần triển khai các phương thức UITextFieldDelegate khác nếu cần, ví dụ:
// func textFieldShouldReturn(_ textField: UITextField) -> Bool
// func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool
}
// Mở rộng MyCustomInputView để tuân thủ UITextFieldDelegate
// MyCustomInputView đóng vai trò là delegate cho UITextField nội bộ
extension MyCustomInputView: UITextFieldDelegate {
// Implement other necessary UITextFieldDelegate methods here
// For example, letting the view controller decide if editing should end on return key
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder() // Ẩn bàn phím
return true // Cho phép kết thúc chỉnh sửa
}
}
Trong ví dụ này, MyCustomInputView
đóng vai trò là cả delegating object (đối với MyCustomInputViewDelegate
của nó) và delegate object (đối với UITextFieldDelegate
của textField
nội bộ). Điều này rất phổ biến trong thực tế: một View có thể là delegate cho các subview của nó và đồng thời là delegating object cho ViewController chứa nó.
Lưu ý cách thuộc tính delegate
được khai báo là weak
. Điều này cực kỳ quan trọng để tránh Reference Cycles. Nếu delegate là một class instance (thường là vậy) và nó giữ tham chiếu mạnh đến MyCustomInputView
, trong khi MyCustomInputView
cũng giữ tham chiếu mạnh đến delegate, cả hai sẽ giữ nhau lại và không bao giờ được giải phóng bộ nhớ, dẫn đến memory leak. Khai báo weak
đảm bảo rằng tham chiếu đến delegate không làm tăng Reference Count của delegate.
Bước 3: Tạo Delegate Object và Thiết lập Delegate
Cuối cùng, chúng ta tạo một ViewController hoặc một class khác sẽ đóng vai trò là delegate. Class này cần tuân thủ MyCustomInputViewDelegate
protocol và cung cấp logic cho các phương thức của nó.
import UIKit
class ViewController: UIViewController {
private let customInputView: MyCustomInputView = {
let view = MyCustomInputView()
view.backgroundColor = .systemGray6
return view
}()
private let resultLabel: UILabel = {
let label = UILabel()
label.text = "Kết quả nhập sẽ hiển thị ở đây"
label.textAlignment = .center
label.numberOfLines = 0
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
setupSubviews()
// MARK: - Thiết lập Delegate
customInputView.delegate = self // Gán ViewController làm delegate cho customInputView
}
private func setupSubviews() {
view.addSubview(customInputView)
view.addSubview(resultLabel)
customInputView.translatesAutoresizingMaskIntoConstraints = false
resultLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
customInputView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
customInputView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
customInputView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
customInputView.heightAnchor.constraint(equalToConstant: 50),
resultLabel.topAnchor.constraint(equalTo: customInputView.bottomAnchor, constant: 20),
resultLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
resultLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
])
}
}
// Mở rộng ViewController để tuân thủ MyCustomInputViewDelegate
extension ViewController: MyCustomInputViewDelegate {
// MARK: - MyCustomInputViewDelegate Methods (Delegate Object implements the protocol)
func myCustomInputViewDidFinishEditing(_ inputView: MyCustomInputView, withText text: String) {
print("ViewController nhận thông báo: Người dùng đã nhập xong text: \(text)")
resultLabel.text = "Bạn đã nhập xong: \(text)"
// Ví dụ: Điều hướng sang màn hình khác sau khi nhập xong
// Điều này liên quan đến Điều hướng trong iOS mà chúng ta đã tìm hiểu:
// https://tuyendung.evotek.vn/dieu-huong-trong-ios-xay-dung-trai-nghiem-nguoi-dung-lien-mach-voi-view-controllers-va-chuyen-doi-view/
// Hoặc cụ thể hơn về View Controllers, Modal, Segues:
// https://tuyendung.evotek.vn/dieu-huong-trong-uikit-view-controllers-modal-va-segues-lo-trinh-ios-developer-2025/
}
func myCustomInputViewTextDidChange(_ inputView: MyCustomInputView, newText text: String) {
print("ViewController nhận thông báo: Text đang thay đổi: \(text)")
// Cập nhật nhãn hiển thị text đang gõ
resultLabel.text = "Đang nhập: \(text)"
}
}
Trong phương thức viewDidLoad
của ViewController
, chúng ta tạo một instance của MyCustomInputView
và gán self
(tức là instance của ViewController
) làm delegate của nó. Vì ViewController
tuân thủ MyCustomInputViewDelegate
protocol, trình biên dịch sẽ đảm bảo rằng nó đã triển khai (hoặc có thể triển khai) tất cả các phương thức cần thiết.
Bây giờ, khi người dùng nhập text vào UITextField
bên trong MyCustomInputView
và kết thúc chỉnh sửa (ví dụ: chạm vào nền hoặc nhấn return nếu đã triển khai textFieldShouldReturn
), phương thức textFieldDidEndEditing
của MyCustomInputView
sẽ được gọi. Bên trong phương thức này, MyCustomInputView
sẽ kiểm tra xem thuộc tính delegate
của nó có khác nil không, và nếu có, nó sẽ gọi phương thức myCustomInputViewDidFinishEditing
trên delegate. ViewController, với vai trò là delegate, sẽ nhận được thông báo này và cập nhật resultLabel
.
Tương tự, khi text trong UITextField thay đổi (sự kiện .editingChanged
), phương thức textFieldDidChange
của MyCustomInputView
sẽ được gọi, và nó sẽ thông báo cho delegate thông qua phương thức myCustomInputViewTextDidChange
.
Delegate trong các Framework của Apple
Như đã đề cập, Delegate Pattern được sử dụng rất phổ biến trong các framework của Apple. Một số ví dụ điển hình:
UITableViewDelegate
vàUITableViewDataSource
: Hai protocol này là cốt lõi để hiển thị dữ liệu trong TableView.UITableViewDataSource
chịu trách nhiệm cung cấp dữ liệu (số lượng section, số lượng row, cell cho mỗi row), cònUITableViewDelegate
xử lý các sự kiện tương tác (chọn row, xóa row, chiều cao row, etc.). ViewController thường là đối tượng đảm nhiệm vai trò delegate và dataSource cho TableView của nó. Đây là một ví dụ tuyệt vời về cách Delegation giúp tách biệt việc hiển thị UI (UITableView
) với logic dữ liệu và tương tác (ViewController).UITextFieldDelegate
: Xử lý các sự kiện liên quan đến việc nhập text trong UITextField, cho phép bạn can thiệp vào quá trình chỉnh sửa, định dạng text, hoặc phản ứng khi người dùng nhấn phím Return.UIApplicationDelegate
: Một trong những delegate quan trọng nhất, xử lý các sự kiện vòng đời của ứng dụng (app lifecycle) như khi ứng dụng khởi chạy, chuyển sang chế độ nền, trở lại foreground, nhận push notification, v.v. Hiểu rõ vòng đời của ứng dụng, cũng như Hiểu rõ Vòng đời của ViewController trong UIKit, là kiến thức nền tảng cho mọi iOS developer.CLLocationManagerDelegate
: Xử lý các cập nhật về vị trí từ GPS.- Các Delegate cho Animation, Networking, Core Data, etc.
Khi làm việc với các framework này, bạn sẽ thường thấy mình cần tạo một class (thường là ViewController) để tuân thủ một hoặc nhiều protocol delegate/dataSource và gán instance của class đó vào thuộc tính delegate/dataSource của component Apple cung cấp.
So sánh Delegate với các cơ chế truyền thông khác
Trong iOS, có nhiều cách để các đối tượng giao tiếp với nhau ngoài Delegation. Một số phổ biến bao gồm:
- Closures/Callbacks: Sử dụng các khối code (block) để thực hiện một hành động sau khi một sự kiện xảy ra.
- Notification Center: Một cơ chế “phát sóng” (broadcast) nơi một đối tượng có thể phát đi một thông báo và nhiều đối tượng khác có thể đăng ký để nhận thông báo đó.
- Key-Value Observing (KVO): Một cơ chế cho phép một đối tượng theo dõi sự thay đổi giá trị của một thuộc tính trên đối tượng khác.
- Target-Action: Thường được sử dụng với các điều khiển UI (như
UIButton
,UISlider
) để gọi một phương thức cụ thể (action) trên một đối tượng (target) khi một sự kiện xảy ra. - Combine/RxSwift: Các framework reactive cho phép xử lý các luồng sự kiện không đồng bộ một cách mạnh mẽ. Chúng ta đã có các bài viết riêng về MVVM với Combine và Khám Phá RxSwift.
Mỗi cơ chế có ưu nhược điểm và phù hợp với các tình huống khác nhau:
- Delegate vs Closures:
- Delegation: Tốt cho các tương tác “một đối một”, khi đối tượng nguồn cần delegate cung cấp nhiều thông tin hoặc xử lý nhiều loại sự kiện khác nhau (nhiều phương thức trong protocol). Protocol cung cấp cấu trúc rõ ràng. Tránh Reference Cycles dễ dàng hơn với
weak
delegate. - Closures: Tốt cho các tương tác “một đối một” đơn giản, khi chỉ cần thông báo một hoặc hai sự kiện cụ thể. Cú pháp ngắn gọn hơn đối với các trường hợp đơn giản. Tuy nhiên, cần cẩn thận với Reference Cycles khi sử dụng capture list (
[weak self]
hoặc[unowned self]
).
- Delegation: Tốt cho các tương tác “một đối một”, khi đối tượng nguồn cần delegate cung cấp nhiều thông tin hoặc xử lý nhiều loại sự kiện khác nhau (nhiều phương thức trong protocol). Protocol cung cấp cấu trúc rõ ràng. Tránh Reference Cycles dễ dàng hơn với
- Delegate vs Notification Center:
- Delegation: Mối quan hệ “một đối một”, người gửi biết (hoặc mong đợi) có một người nhận cụ thể tuân thủ protocol. Có phản hồi trực tiếp (delegate có thể trả về giá trị hoặc quyết định hành vi).
- Notification Center: Mối quan hệ “một nhiều” hoặc “nhiều nhiều”. Người gửi không cần biết ai sẽ nhận thông báo, chỉ cần phát đi. Không có phản hồi trực tiếp. Thường dùng cho các sự kiện hệ thống hoặc giao tiếp giữa các module không liên quan trực tiếp.
Delegate Pattern thường là lựa chọn tốt khi bạn cần một giao tiếp có cấu trúc, hai chiều (gọi phương thức trên delegate, delegate trả về giá trị hoặc thực hiện hành động) và chỉ giữa hai đối tượng được xác định rõ ràng.
Best Practices khi sử dụng Delegate Pattern
Để sử dụng Delegate Pattern một cách hiệu quả và an toàn, hãy lưu ý các điểm sau:
- Sử dụng Protocol: Luôn định nghĩa protocol cho delegate. Điều này đảm bảo tính linh hoạt và tách rời.
- Khai báo Delegate là
weak
: Đối với các thuộc tính delegate kiểu class, luôn khai báo chúng làweak var
để ngăn Strong Reference Cycles, đặc biệt khi delegate là một ViewController chứa đối tượng nguồn. - Sử dụng
AnyObject
cho Protocol: Thêm: AnyObject
vào định nghĩa protocol nếu bạn muốn thuộc tính delegate có thể làweak
. - Kiểm tra delegate trước khi gọi phương thức: Vì thuộc tính delegate là optional, luôn sử dụng optional chaining (
delegate?.<methodName>(...)
) hoặc kiểm tra nil trước khi gọi các phương thức của delegate. - Đặt tên protocol rõ ràng: Tên protocol delegate thường theo quy ước
<ClassName>Delegate
(ví dụ:UITableViewDelegate
,MyCustomInputViewDelegate
). - Đặt tên phương thức delegate rõ ràng: Tên phương thức thường bắt đầu bằng tên của đối tượng nguồn để dễ hiểu ngữ cảnh (ví dụ:
tableView(_:numberOfRowsInSection:)
,myCustomInputViewDidFinishEditing(_:withText:)
). Tham số đầu tiên của phương thức delegate thường là chính đối tượng nguồn. - Không đưa quá nhiều phương thức vào một protocol: Nếu protocol của bạn trở nên quá lớn với hàng chục phương thức, có thể xem xét chia nhỏ nó thành nhiều protocol nhỏ hơn hoặc sử dụng các cơ chế truyền thông khác cho một số nhiệm vụ.
Việc áp dụng các thực hành tốt này giúp code của bạn dễ đọc, dễ bảo trì và tránh được các lỗi phổ biến như memory leak.
Một vài lưu ý thêm
Mặc dù Delegate Pattern rất mạnh mẽ, nhưng nó có thể trở nên cồng kềnh khi đối tượng delegate phải tuân thủ nhiều protocol khác nhau (ví dụ: một ViewController vừa là delegate, data source của nhiều TableView, vừa là delegate của Text Field, etc.). Trong các kiến trúc phức tạp hơn như MVVM (với Combine hoặc RxSwift) hoặc VIPER/TCA (Lựa Chọn Kiến Trúc iOS), trách nhiệm của ViewController có thể được giảm bớt bằng cách chuyển việc tuân thủ các protocol delegate/dataSource sang các class hoặc struct riêng biệt (ví dụ: một class TableViewManager
).
Tuy nhiên, dù sử dụng kiến trúc nào, việc hiểu cách Delegate Pattern hoạt động vẫn là nền tảng quan trọng, bởi vì các UI component của Apple vẫn sử dụng nó rất nhiều.
Kết luận
Delegate Pattern là một mẫu thiết kế cơ bản và cực kỳ quan trọng trong phát triển ứng dụng iOS. Nó cung cấp một cách có cấu trúc và linh hoạt để các đối tượng giao tiếp với nhau, cho phép tách rời trách nhiệm, tái sử dụng code và xử lý các sự kiện một cách hiệu quả. Bằng cách nắm vững cách định nghĩa protocol, tạo đối tượng nguồn và đối tượng được ủy quyền, cùng với các thực hành tốt như sử dụng weak
delegate, bạn sẽ có thể làm việc hiệu quả với các framework của Apple và xây dựng các component tùy chỉnh của riêng mình theo cách “kiểu iOS”.
Việc hiểu sâu về Delegation sẽ là bước tiến lớn trên Lộ trình học Lập trình viên iOS 2025 của bạn. Hãy thực hành bằng cách sử dụng các delegate có sẵn trong UIKit và thử tạo delegate tùy chỉnh cho các View hoặc class của riêng mình!