Delegate: คืออะไร รูปแบบการมอบหมายและทำงานอย่างไรบน iOS

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-03-17 เวลาอ่าน: 10 นาที

Delegate คือรูปแบบการออกแบบที่วัตถุหนึ่งมอบหมายการดำเนินงานให้กับอีกวัตถุหนึ่งผ่านโปรโตคอลที่มีเมธอดที่กำหนดไว้ล่วงหน้า ในการพัฒนา iOS Delegate เป็นหนึ่งในรูปแบบพื้นฐานของ Cocoa Touch ที่ใช้สำหรับการแจ้งเตือนแบบอะซิงโครนัสโดยไม่มีการเชื่อมโยงโดยตรงระหว่างผู้ส่งและผู้รับ ตาม Apple Documentation (2025) การมอบหมายถูกใช้ใน Foundation และ UIKit สำหรับจัดการเหตุการณ์ของตาราง คำขอเครือข่าย และการจัดการตำแหน่งที่ตั้ง รูปแบบนี้ช่วยให้ส่วนประกอบมีการเชื่อมโยงแบบหลวม ๆ และสามารถนำโค้ดกลับมาใช้ใหม่ได้

ประเด็นสำคัญ

  • Delegate คือรูปแบบที่วัตถุมอบหมายการจัดการเหตุการณ์ให้กับอีกวัตถุหนึ่งผ่านโปรโตคอล
  • Protocol ใน Swift กำหนดชุดเมธอดที่ delegate ต้องหรือสามารถimplementได้
  • Weak reference จำเป็นสำหรับคุณสมบัติ delegate เพื่อหลีกเลี่ยง retain cycle
  • @objc optional ทำให้เมธอดของโปรโตคอลเป็นตัวเลือกสำหรับการimplement
  • URLSessionDelegate คือ delegate แบบอะซิงโครนัสสำหรับจัดการเหตุการณ์คำขอเครือข่าย

Delegate คืออะไร?

Delegate (ผู้รับมอบหมาย) คือวัตถุที่implementโปรโตคอลเฉพาะและได้รับการแจ้งเตือนเกี่ยวกับเหตุการณ์ของวัตถุอื่น รูปแบบการมอบหมายเป็นทางเลือกแทนการสืบทอด: แทนที่จะสร้างคลาสย่อยเพื่อoverrideเมธอด วัตถุจะมอบหมายการจัดการเหตุการณ์ให้กับวัตถุภายนอก ในiOS การมอบหมายถูกimplementผ่านโปรโตคอล Swift พร้อมเมธอดที่จำเป็นและเป็นตัวเลือก คุณสมบัติ delegate จะถูกประกาศเป็น weak var เสมอเพื่อหลีกเลี่ยงการอ้างอิงแบบวงกลมระหว่างวัตถุ

การกำหนดโปรโตคอล Delegate

โปรโตคอล delegate กำหนดสัญญาการโต้ตอบระหว่างวัตถุ เมธอดที่จำเป็น ต้องถูกimplementโดยผู้รับมอบหมาย มิฉะนั้นโค้ดจะไม่คอมไพล์ เมธอดที่เป็นตัวเลือก จะถูกทำเครื่องหมายด้วยแอตทริบิวต์ @objc optional และอนุญาตให้ผู้รับมอบหมายตอบสนองต่อเฉพาะเหตุการณ์ที่เกี่ยวข้อง ชื่อเมธอดเป็นไปตามธรรมเนียม: พารามิเตอร์แรกคือวัตถุผู้ส่ง พารามิเตอร์ที่สองคือข้อมูลเหตุการณ์ ตัวอย่างเช่น tableView(_:didSelectRowAt:) ระบุว่าผู้ส่งคือ UITableView และข้อมูลคือดัชนีของแถวที่เลือก

swift
// โปรโตคอล Delegate
protocol DownloadManagerDelegate: AnyObject {
    func downloadManager(_ manager: DownloadManager,
                          didFinishWith data: Data)
    func downloadManager(_ manager: DownloadManager,
                          didFailWith error: Error)

    @objc optional func downloadManager(_ manager: DownloadManager,
                                didUpdateProgress progress: Float)
}

// คลาสที่ใช้ Delegate
class DownloadManager {
    weak var delegate: DownloadManagerDelegate?

    func startDownload(from url: URL) {
        URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
            guard let self else { return }
            if let error = error {
                self.delegate?.downloadManager(self, didFailWith: error)
            } else if let data = data {
                self.delegate?.downloadManager(self, didFinishWith: data)
            }
        }.resume()
    }
}

การอ้างอิงแบบอ่อนไปยัง Delegate

คุณสมบัติ delegate ต้องถูกประกาศเป็น weak var เพื่อป้องกัน retain cycle หากการอ้างอิงเป็นแบบแข็ง ผู้รับมอบหมายและวัตถุที่มอบหมายจะยึดซึ่งกันและกัน และ ARC จะไม่สามารถ freeing หน่วยความจำของพวกมันได้ โปรโตคอล delegate สืบทอด AnyObject (เฉพาะคลาส) ซึ่งอนุญาตให้ใช้ weak ได้ โครงสร้างและenumไม่สามารถเป็นผู้รับมอบหมายได้เนื่องจากความหมายของค่า ทางเลือกสำหรับชนิดค่าคือ callback closures

swift
class ViewController: DownloadManagerDelegate {
    let manager = DownloadManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        manager.delegate = self // weak — ไม่มี retain cycle
        manager.startDownload(from: url)
    }

    func downloadManager(_ manager: DownloadManager, didFinishWith data: Data) {
        processData(data)
    }

    func downloadManager(_ manager: DownloadManager, didFailWith error: Error) {
        showError(error)
    }
}

รูปแบบ Delegate ทำงานอย่างไรใน iOS?

รูปแบบ Delegate ทำงานแบบหนึ่งต่อหนึ่ง: วัตถุผู้ส่งหนึ่งรายการสามารถมี delegate ได้เพียงรายการเดียวในเวลาใดเวลาหนึ่ง เมื่อเหตุการณ์เกิดขึ้น ผู้ส่งจะตรวจสอบว่า delegate ถูกตั้งไว้หรือไม่ และเรียกเมธอดโปรโตคอลที่เกี่ยวข้อง ข้อได้เปรียบเหนือการเรียกโดยตรงคือผู้ส่งไม่รู้จักชนิดของ delegate เพียงแต่ว่ามันสอดคล้องกับโปรโตคอล ซึ่งเป็นไปตามหลักการการกลับด้านการพึ่งพา (DIP) จาก SOLID

วงจรชีวิตของผู้รับมอบหมาย

ผู้รับมอบหมายถูกกำหนดผ่านการกำหนด: someObject.delegate = self เมื่อผู้รับมอบหมายถูกดีโลเคท คุณสมบัติจะกลายเป็น nil โดยอัตโนมัติเนื่องจากความหมายแบบ weak ก่อนที่จะเรียกเมธอดของผู้รับมอบหมาย ผู้รับมอบหมายจะถูกตรวจสอบผ่านการเชื่อมโยงแบบตัวเลือก: delegate?.method() ถ้า delegate เป็น nil การเรียกจะถูกข้ามไปโดยไม่พัง สำหรับเมธอดที่เป็นตัวเลือกของโปรโตคอล จะใช้การตรวจสอบเพิ่มเติม: delegate?.responds(to: #selector(...)) แม้ว่าใน Swift การตรวจสอบนี้มักจะเป็นแบบโดยนัยผ่านการประกาศเมธอดที่เป็นตัวเลือก

การแจ้งเตือนแบบอะซิงโครนัสผ่าน Delegate

ในสภาพแวดล้อมแบบหลายเธรด delegate ถูกใช้สำหรับการส่งคืนผลลัพธ์แบบอะซิงโครนัส URLSession ให้ URLSessionDelegate พร้อมเมธอดที่ถูกเรียกเมื่อได้รับข้อมูล หมดเวลา หรือข้อผิดพลาดการรับรองความถูกต้อง เมธอดของ delegate ถูกดำเนินการในคิวพื้นหลังของ URLSession ดังนั้นจึงจำเป็นต้องส่งไปยังคิวหลักสำหรับการอัปเดต UI delegate แบบอะซิงโครนัสไม่บล็อกเธรดที่เรียก ทำให้งานอื่น ๆ สามารถดำเนินต่อไปได้

swift
class NetworkService: NSObject, URLSessionDataDelegate {
    private lazy var session = URLSession(
        configuration: .default,
        delegate: self,
        delegateQueue: OperationQueue()
    )
    private var receivedData = Data()

    func urlSession(_ session: URLSession,
                    dataTask: URLSessionDataTask,
                    didReceive data: Data) {
        receivedData.append(data)
        let progress = Float(receivedData.count) / Float(expectedSize)
        DispatchQueue.main.async {
            self.progressHandler?(progress)
        }
    }

    func urlSession(_ session: URLSession,
                    task: URLSessionTask,
                    didCompleteWithError error: Error?) {
        if let error = error {
            delegate?.networkService(self, didFailWith: error)
        } else {
            delegate?.networkService(self, didReceive: receivedData)
        }
    }
}

Delegate vs Callback: การเปรียบเทียบแนวทาง

Delegate และ Callback แก้ปัญหาเดียวกัน — การแจ้งเตือนแบบอะซิงโครนัส — แต่ในวิธีที่ต่างกัน Delegate ใช้โปรโตคอลพร้อมเมธอดที่มีชื่อ Callback ใช้ closure พร้อมการจับบริบท ตัวเลือกขึ้นอยู่กับจำนวนเหตุการณ์ ความซับซ้อนของลายเซ็น และความชอบทางสถาปัตยกรรม Apple แนะนำ Delegate สำหรับ API ที่มีหลายเหตุการณ์ (UITableView — 20+ เมธอด) และ Callback สำหรับการเสร็จสิ้นครั้งเดียว

เมื่อใด Delegate ชนะ

Delegate เหนือกว่าเมื่อต้องจัดการกับหลายเหตุการณ์ที่แตกต่างจากแหล่งเดียว ตัวอย่างเช่น CLLocationManager แจ้งผู้รับมอบหมายเกี่ยวกับการเปลี่ยนแปลงตำแหน่ง ข้อผิดพลาดสิทธิ์ การเข้า/ออกจากรั้วภูมิศาสตร์ และการเปลี่ยนแปลงสถานะบริการ แต่ละเหตุการณ์เป็นเมธอดโปรโตคอลแยกต่างหากที่มีชื่อชัดเจนและพารามิเตอร์ที่ระบุชนิด Delegate ยังสะดวกสำหรับการกำหนดค่าพฤติกรรม (เมธอด should, will, did)

เมื่อใด Callback ชนะ

Callback ง่ายกว่าสำหรับคำขอครั้งเดียวที่มีผลลัพธ์เดียว Completion handler ใน URLSession.dataTask ใช้หนึ่งบรรทัดที่จุดเรียกเทียบกับอย่างน้อยสามเมธอดของโปรโตคอล Callback ยังเป็นธรรมชาติมากกว่าสำหรับลูกโซ่ฟังก์ชัน (map, flatMap, async/await) อย่างไรก็ตาม ด้วยการซ้อนมากกว่า 2-3 ระดับ Callback จะกลายเป็น Callback Hell ในขณะที่ delegate ยังคงราบเรียบเสมอ

Delegate ในตัวใน iOS SDK

iOS SDK ประกอบด้วยโปรโตคอล delegate ในตัวหลายสิบรายการสำหรับระบบย่อยต่าง ๆ แต่ละรายการถูกออกแบบสำหรับสถานการณ์การโต้ตอบเฉพาะ ตาม Apple Documentation (2025) delegate ที่ใช้มากที่สุดคือ UITableViewDelegate, UITextFieldDelegate, CLLocationManagerDelegate, URLSessionDelegate และ UNUserNotificationCenterDelegate โปรโตคอลเหล่านี้มี 3 ถึง 30 เมธอดพร้อมระดับความจำเป็นที่แตกต่างกัน

UITableViewDelegate

UITableViewDelegate จัดการลักษณะที่ปรากฏและพฤติกรรมของเซลล์ตาราง ประกอบด้วยเมธอดสำหรับจัดการการเลือกแถว การกำหนดความสูงของเซลล์ มุมมองส่วนหัว/ท้ายแบบกำหนดเอง และการดำเนินการปัด ทุกเมธอดของโปรโตคอลเป็นตัวเลือก ทำให้สามารถimplementเฉพาะฟังก์ชันที่ต้องการได้ หากไม่มี delegate ตารางจะทำงานด้วยการตั้งค่าเริ่มต้น ในอดีต delegate ถูกรวมกับ UITableViewDataSource

URLSessionDelegate

URLSessionDelegate ให้การควบคุมโดยละเอียดเหนือคำขอ HTTP เมธอดของ delegate จะถูกเรียกเมื่อได้รับการตอบสนองจากเซิร์ฟเวอร์ ข้อมูลมาถึง หรือดาวน์โหลดเสร็จสมบูรณ์ โปรโตคอลย่อยเฉพาะทาง URLSessionTaskDelegate และ URLSessionDataDelegate ขยายฟังก์ชันพื้นฐานสำหรับชนิดงานเฉพาะ จำเป็นต้องใช้ delegate เพื่อรองรับการดาวน์โหลดพื้นหลัง ใบรับรอง SSL และการจัดการการเปลี่ยนเส้นทางแบบกำหนดเอง

Delegateเมธอดวัตถุประสงค์
UITableViewDelegate25ลักษณะที่ปรากฏและการโต้ตอบของตาราง
UITextFieldDelegate8การจัดการการป้อนข้อความและแป้นพิมพ์
CLLocationManagerDelegate12การอัปเดตตำแหน่งและรั้วภูมิศาสตร์
URLSessionDelegate6การจัดการเซสชัน HTTP และใบรับรอง
UNUserNotificationCenterDelegate4การจัดการการแจ้งเตือน Push ในเบื้องหน้า

การจัดการหน่วยความจำกับ Delegate

การจัดการหน่วยความจำเป็นลักษณะสำคัญของการทำงานกับ delegate ใน iOS ARC (การนับการอ้างอิงอัตโนมัติ) จัดการหน่วยความจำโดยอัตโนมัติ แต่เฉพาะกับการใช้การอ้างอิงแบบ weak/unowned อย่างถูกต้อง การละเมิดกฎนำไปสู่การรั่วไหลของหน่วยความจำหรือการดีโลเคทก่อนกำหนด delegate ที่ประกาศเป็น strong จะสร้าง retain cycle หากเจ้าของ delegate ยังคงการอ้างอิงไปยังวัตถุที่มอบหมาย

Retain Cycle ผ่าน Delegate

Retain cycle เกิดขึ้นเมื่อวัตถุ A (เจ้าของ) ตั้งตัวเองเป็น delegate ของวัตถุ B และ B เก็บการอ้างอิงแบบแข็งไปยัง delegate ตัวอย่าง: ViewController สร้าง URLSession ตั้งตัวเองเป็น delegate ของเซสชัน แต่ URLSession โดยค่าเริ่มต้นเก็บการอ้างอิงแบบแข็งไปยัง delegate หากไม่ได้ระบุ delegateQueue วิธีแก้คือตรวจสอบเอกสาร API สำหรับชนิดการอ้างอิง delegate (weak หรือ strong) เสมอ และตั้ง delegate เป็น nil อย่างชัดเจนใน deinit

swift
class SafeViewController: UIViewController {
    private var session: URLSession?
    private var service: NetworkService?

    override func viewDidLoad() {
        super.viewDidLoad()
        service = NetworkService()
        service?.delegate = self
    }

    deinit {
        // ตั้ง delegate เป็น nil ใน deinit — best practice
        service?.delegate = nil
        session?.invalidateAndCancel()
    }
}

// URLSession พร้อม weak delegate ผ่าน NSObject
class WeakDelegateSession: NSObject {
    private weak var delegate: URLSessionDelegate?

    func createSession() -> URLSession {
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
        return URLSession(
            configuration: .default,
            delegate: self,
            delegateQueue: queue
        )
    }
}

การตรวจสอบ delegate อย่างปลอดภัยก่อนเรียก

ก่อนเรียกเมธอดของ delegate คุณต้องตรวจสอบว่า delegate มีอยู่ (ไม่ใช่ nil) และimplementเมธอดที่ถูกเรียก สำหรับเมธอดที่จำเป็นของโปรโตคอล ไม่จำเป็นต้องตรวจสอบ — คอมไพเลอร์รับประกันการimplement สำหรับ เมธอดที่เป็นตัวเลือก ให้ใช้ respond(to:) หรือการเชื่อมโยงแบบตัวเลือก ถ้า delegate ถูกดีโลเคท การอ้างอิงแบบ weak จะกลายเป็น nil โดยอัตโนมัติ และการเรียก delegate จะถูกข้ามไป ซึ่งเป็นพฤติกรรมที่ปลอดภัยที่ไม่ต้องการการจัดการเพิ่มเติม

ข้อผิดพลาดทั่วไปในการimplement Delegate

นักพัฒนามักทำผิดพลาดเมื่อทำงานกับรูปแบบ Delegate โดยเฉพาะในระยะแรกของการเรียนรู้ iOS ที่พบบ่อยที่สุด ได้แก่: retain cycle จาก delegate แบบ strong, ลืมเรียก delegate?.method(), ลายเซ็นเมธอดโปรโตคอลไม่ถูกต้อง, การตั้ง delegate หลังจากเริ่มดำเนินการ และการชนกันของหลายเธรด มาดูแต่ละข้อผิดพลาดและวิธีป้องกัน

การอ้างอิงแบบ Strong แทน Weak

ข้อผิดพลาดที่สำคัญที่สุดคือการประกาศคุณสมบัติ delegate เป็น strong var แทน weak var ซึ่งสร้าง retain cycle ที่ทั้ง delegate และวัตถุที่มอบหมายไม่สามารถถูก freeing ได้ ผลกระทบ: หน่วยความจำรั่วไหล แอปช้าลง และบักที่ซ่อนอยู่ วิธีแก้: ใช้ weak var สำหรับ delegate เสมอ และให้โปรโตคอลสืบทอดจาก AnyObject เพื่อป้องกันการใช้ชนิดค่าเป็น delegate

การตั้ง delegate หลังจากเริ่มดำเนินการ

ถ้า delegate ถูกตั้งหลังจากเรียกเมธอดแบบอะซิงโครนัส เหตุการณ์แรกอาจสูญหาย ตัวอย่าง: การเรียก startDownload() ก่อนกำหนด manager.delegate = self ส่งผลให้สูญเสีย callback การเสร็จสิ้นหากการดาวน์โหลดดำเนินการแบบซิงโครนัสหรือเร็วมาก วิธีแก้: ตั้ง delegate ก่อนเรียกเมธอดแบบอะซิงโครนัส และบันทึกลำดับการเริ่มต้นในความคิดเห็นของโปรโตคอล

คำถามที่พบบ่อย

ทำไม delegate ถูกประกาศเป็น weak?

Weak ป้องกัน retain cycle ระหว่าง delegate และวัตถุที่มอบหมาย หากการอ้างอิงเป็นแบบแข็ง วัตถุจะยึดซึ่งกันและกัน และ ARC จะไม่สามารถ freeing พวกมันได้ การอ้างอิงแบบ weak จะกลายเป็น nil โดยอัตโนมัติเมื่อ delegate ถูกดีโลเคท นี่เป็นแนวปฏิบัติมาตรฐานของ Cocoa Touch ตั้งแต่การถือกำเนิดของ Objective-C และถูกเก็บไว้ใน Swift เพื่อความเข้ากันได้ย้อนหลัง

ความแตกต่างระหว่าง delegate และ dataSource คืออะไร?

Delegate จัดการเหตุการณ์และพฤติกรรม (ความสูงของเซลล์ การตอบสนองต่อการแตะ) DataSource ให้ข้อมูลสำหรับการแสดงผล (จำนวนแถว เซลล์) ผู้รับมอบหมายตอบคำถาม “อย่างไร?” dataSource ตอบคำถาม “อะไร?” ในiOS ทั้งคู่ถูกimplementผ่านโปรโตคอล มักอยู่ในคอนโทรลเลอร์เดียวกัน แต่แยกแนวคิดกัน

สามารถใช้ struct เป็น delegate ได้หรือไม่?

ไม่ได้ ถ้าโปรโตคอลสืบทอดจาก AnyObject (โปรโตคอลคลาส) การอ้างอิงแบบ weak ใช้ได้เฉพาะกับชนิดอ้างอิง (คลาส) เท่านั้น สำหรับชนิดค่า (struct, enum) ให้ใช้ callback closures หรือคลาส wrapper แยกต่างหาก ถ้าคุณควบคุมโปรโตคอล คุณสามารถหลีกเลี่ยงการสืบทอด AnyObject แต่แล้ว weak จะถูกห้าม — เลือกอย่างมีสติระหว่าง delegate แบบ weak และ delegate แบบ struct

เมธอด responds(to:) คืออะไรและทำไมจึงจำเป็น?

responds(to:) เป็นเมธอดของ NSObjectProtocol ที่ตรวจสอบว่าวัตถุimplementซีเล็กเตอร์ที่ระบุหรือไม่ ใช้สำหรับตรวจสอบเมธอดที่เป็นตัวเลือก @objc ของโปรโตคอลก่อนเรียก หากไม่มีการตรวจสอบนี้ การเรียกเมธอดที่เป็นตัวเลือกที่ยังไม่ได้implementจะทำให้เกิด NSInvalidArgumentException ในSwift สำหรับโปรโตคอลที่มี @objc optional การตรวจสอบสามารถเป็นแบบโดยนัยผ่าน optional binding

Delegate เป็น singleton หรือไม่?

ไม่ delegate เป็นรูปแบบการมอบหมาย ไม่ใช่ singleton แตกต่างจาก singleton ผู้รับมอบหมายสามารถถูกแทนที่ได้ในruntime และมีอยู่ในอินสแตนซ์เดียวสำหรับแต่ละวัตถุที่มอบหมาย วัตถุหนึ่งสามารถเป็น delegate สำหรับผู้ส่งหลายราย Singleton เป็นรูปแบบการสร้างที่รับประกันอินสแตนซ์คลาสเดียว ซึ่งไม่เกี่ยวข้องกับการมอบหมาย

สรุป

  • Delegate เป็นรูปแบบที่วัตถุมอบหมายการจัดการเหตุการณ์ให้กับอีกวัตถุหนึ่งผ่านโปรโตคอลพร้อมเมธอดที่ระบุชนิด
  • Weak var จำเป็นสำหรับคุณสมบัติ delegate เพื่อป้องกัน retain cycle และการรั่วไหลของหน่วยความจำ
  • Protocol กำหนดเมธอดการมอบหมายที่จำเป็นและเป็นตัวเลือก (@objc optional)
  • iOS SDK มีโปรโตคอล delegate ในตัว 15+ รายการ: UITableViewDelegate, URLSessionDelegate, CLLocationManagerDelegate
  • Delegate เหนือกว่า callback เมื่อมี 3+ เหตุการณ์ที่แตกต่างจากแหล่งเดียว (CLLocationManager)
  • Delegate แบบอะซิงโครนัส ใช้ใน URLSession สำหรับรับข้อมูลและความคืบหน้าโดยไม่บล็อกเธรด
  • ตั้ง delegate ก่อนเริ่มดำเนินการแบบอะซิงโครนัสและตั้งเป็น nil ใน deinit เพื่อการจัดการหน่วยความจำที่ปลอดภัย

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม