Delegate: шта је то, образац делегирања и како ради на iOS-у

Аутор: IT Sectr Објављено: 2026-03-17 Време читања: 10 мин

Delegate — то је образац пројектовања при којем један објекат делегира извршење задатака другом објекту преко протокола са унапред дефинисаним методама. У iOS развоју, Delegate је један од фундаменталних образаца Cocoa Touch-а, који се користи за асинхроно обавештавање без директне везе између пошиљаоца и примаоца. Према Apple Documentation (2025), делегирање се примењује у Foundation и UIKit-у за обраду догађаја табела, мрежних захтева и управљање локацијом. Образац обезбеђује слабу повезаност компоненти и поновно коришћење кода.

Главно

  • Delegate — образац при којем објекат поверава обраду догађаја другом објекту преко протокола.
  • Protocol у Swift-у дефинише скуп метода које delegate мора или може да имплементира.
  • Weak reference је обавезна за својство delegate како би се избегао retain cycle.
  • @objc optional омогућава да методе протокола буду опционе за имплементацију.
  • URLSessionDelegate — асинхрони delegate за обраду догађаја мрежног захтева.

Шта је Delegate?

Delegate (делегат) — то је објекат који имплементира одређени протокол и прима обавештења о догађајима другог објекта. Образац Delegation је алтернатива наслеђивању: уместо креирања подкласе за превазилажење метода, објекат делегира обраду догађаја спољном објекту. У iOS-у, delegation је имплементиран кроз Swift протоколе са обавезним и опционим методама. Својство delegate се увек декларише као weak var како би се избегле цикличне референце између објеката.

Дефинисање протокола Delegate

Протокол delegate дефинише уговор о интеракцији између објеката. Обавезне методе морају бити имплементиране од стране делегата, иначе код неће бити компајлиран. Опционе методе се обележавају атрибутом @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 не би могао да ослободи њихову меморију. Протоколи delegate наслеђују AnyObject (само за класе), што омогућава употребу weak. Структуре и enum не могу бити делегати због семантике вредности. Алтернатива за value types — 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 постављен и позива одговарајућу методу протокола. Предност у односу на директни позив — пошиљалац не зна тип делегата, само да одговара протоколу. Ово поштује принцип инверзије зависности (DIP) из SOLID-а.

Животни циклус делегата

Делегат се поставља додељивањем: someObject.delegate = self. При деалокацији делегата, својство аутоматски постаје nil због weak семантике. Пре позива методе delegate се проверава кроз optional chaining: delegate?.method(). Ако је delegate nil — позив се игнорише без crash-а. За опционе методе протокола користи се додатна провера: delegate?.responds(to: #selector(...)), иако је у Swift-у ова провера обично имплицитна кроз декларацију опционе методе.

Асинхроно обавештавање путем Delegate

У вишенитном окружењу, delegate се користи за асинхроно враћање резултата. URLSession пружа URLSessionDelegate са методама које се позивају при пријему података, тајмауту или грешци аутентификације. Delegate методе се извршавају у позадинској редици URLSession-а, па је потребна диспечерација на main queue за 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 — затварање са хватањем контекста. Избор зависи од броја догађаја, сложености потписа и архитектонских преференција. 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 увек остаје раван.

Уграђени делегати у iOS SDK

iOS SDK садржи десетине уграђених delegate протокола за различите подсистеме. Сваки од њих је дизајниран за одређени сценарио интеракције. Према Apple Documentation (2025), најчешће коришћени делегати су UITableViewDelegate, UITextFieldDelegate, CLLocationManagerDelegate, URLSessionDelegate и UNUserNotificationCenterDelegate. Ови протоколи садрже између 3 и 30 метода са различитим степеном обавезности.

UITableViewDelegate

UITableViewDelegate управља изгледом и понашањем ћелија табеле. Садржи методе за обраду селекције редова, подешавање висине ћелија, прилагођене приказе header/footer и swipe акције. Све методе протокола су опционе, што омогућава имплементацију само потребне функционалности. У одсуству делегата, табела ради са подразумеваним поставкама. Историјски, delegate се комбиновао са UITableViewDataSource.

URLSessionDelegate

URLSessionDelegate обезбеђује детаљну контролу над HTTP захтевима. Методе делегата се позивају при пријему одговора сервера, података, завршетку преузимања. Специјализовани подпротоколи URLSessionTaskDelegate и URLSessionDataDelegate проширују основну функционалност за одређене типове задатака. Delegate је потребан за подршку позадинских преузимања, SSL сертификата и прилагођене обраде преусмеравања.

DelegateМетодаНамена
UITableViewDelegate25Изглед и интеракција са табелом
UITextFieldDelegate8Обрада уноса текста и тастатуре
CLLocationManagerDelegate12Ажурирање локације и геозоне
URLSessionDelegate6Управљање HTTP сесијом и сертификатима
UNUserNotificationCenterDelegate4Обрада push обавештења у предњем плану

Управљање меморијом при раду са Delegate

Управљање меморијом је критичан аспект рада са delegate-ом у iOS-у. ARC (Automatic Reference Counting) аутоматски управља меморијом, али само уз правилну употребу weak/unowned референци. Кршење правила доводи до цурења меморије или превремене деалокације. Delegate декларисан као strong ствара retain cycle ако власник делегата такође чува референцу на делегирајући објекат.

Retain cycle путем Delegate

Retain cycle настаје када објекат A (власник) постави себе као делегата објекта B, а B чува јаку референцу на delegate. Пример: ViewController креира URLSession, поставља self као делегата сесије, али URLSession подразумевано чува јаку референцу на delegate ако није наведен delegateQueue. Решење — увек проверавајте документацију API-ја за тип референце на delegate (weak или strong) и експлицитно поништите delegate у 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 у 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 пре позива

Пре позива методе делегата потребно је проверити да делегат постоји (није nil) и да имплементира позивану методу. За обавезне методе протокола провера није потребна — компајлер гарантује имплементацију. За опционе методе користите respond(to:) или optional chaining. Ако је делегат деалоциран, weak референца аутоматски постаје nil, а позив делегата се игнорише. Ово је безбедно понашање које не захтева додатну обраду.

Типичне грешке при имплементацији Delegate

Програмери често праве грешке при раду са обрасцем Delegate, посебно у почетним фазама учења iOS-а. Најчешће: retain cycle због strong delegate, заборављање позива delegate?.method(), нетачан потпис метода протокола, постављање delegate након почетка операције и вишенитне колизије. Размотримо сваку грешку и начине њеног спречавања.

Strong reference уместо weak

Најкритичнија грешка — декларисање својства delegate као strong var уместо weak var. Ово ствара retain cycle у којем ни делегат ни делегирајући објекат не могу бити ослобођени. Последице: цурење меморије, успоравање апликације и скривени багови. Решење: увек користите weak var за delegate, а протокол наследите од AnyObject-а да бисте искључили употребу value types као делегата.

Постављање delegate након покретања операције

Ако се delegate постави након позива асинхроне методе, први догађаји могу бити изгубљени. Пример: позив startDownload() пре доделе manager.delegate = self доводи до губитка callback-а о завршетку, ако се преузимање извршава синхроно или веома брзо. Решење: поставите delegate пре позива асинхроне методе и документујте редослед иницијализације у коментарима протокола.

Често постављана питања

Зашто се delegate декларише као weak?

Weak спречава retain cycle између делегата и делегирајућег објекта. Ако би референца била strong, објекти би држали једни друге, и ARC не би могао да их ослободи. Weak-референца аутоматски постаје nil при деалокацији делегата. Ово је стандардна пракса Cocoa Touch-а од појаве Objective-C-а и задржана је у Swift-у ради уназад компатибилности.

Која је разлика између delegate и dataSource?

Delegate обрађује догађаје и управља понашањем (висина ћелија, реакција на кликове). DataSource обезбеђује податке за приказ (број редова, ћелије). Delegate одговара на питање „како?", dataSource — на питање „шта?". У iOS-у, оба се имплементирају кроз протоколе, често у истом контролеру, али су концептуално одвојени.

Може ли се struct користити као delegate?

Не, ако протокол наслеђује AnyObject (класни протокол). Weak-референце су доступне само за reference types (класе). За value types (struct, enum) користите callback затварања или посебну класу-омотач. Ако контролишете протокол, можете да не наследите AnyObject, али тада је weak забрањен — свесно бирајте између weak-делегата и struct-делегата.

Шта је метода responds(to:) и чему служи?

responds(to:) — метода NSObjectProtocol-а која проверава да ли објекат имплементира наведени селектор. Користи се за проверу опционих @objc метода протокола пре позива. Без ове провере, позив неимплементиране опционе методе ће довести до NSInvalidArgumentException-а. У Swift-у, за протоколе са @objc optional, провера може бити имплицитна кроз optional binding.

Да ли је Delegate синглтон?

Не, delegate је образац делегирања, а не синглтон. За разлику од синглтона, делегат може бити замењен у runtime-у и постоји у једној инстанци за сваки делегирајући објекат. Један објекат може бити делегат за више пошиљалаца. Синглтон је креативни образац који гарантује јединствену инстанцу класе и нема никакве везе са делегирањем.

Резиме

  • Delegate — образац при којем објекат делегира обраду догађаја другом објекту преко протокола са типизираним методама.
  • Weak var је обавезан за својство delegate ради спречавања retain cycle-а и цурења меморије.
  • Протокол дефинише обавезне и опционе (@objc optional) методе делегирања.
  • iOS SDK садржи 15+ уграђених delegate протокола: UITableViewDelegate, URLSessionDelegate, CLLocationManagerDelegate.
  • Delegate је пожељнији од callback-а код 3+ различита догађаја из једног извора (CLLocationManager).
  • Асинхрони delegate се користи у URLSession-у за добијање података и напретка без блокирања нити.
  • Поставите delegate пре покретања асинхроне операције и поништите га у deinit-у за безбедан рад са меморијом.

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође