Weak Reference — шта је то, синтакса и примена у мобилном развоју

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

Weak Reference (слаба референца) — је референца на објекат која не повећава његов бројач задржавања у ARC-у. Према Apple Swift Language Guide, 2026, weak-референце се декларишу са кључном речи weak и увек имају опционални тип. Када се објекат ослободи, све weak-референце на њега се аутоматски постављају на nil, што спречава висеће показиваче и чини слабе референце безбедним механизмом за прекидање retain cycle-а.

Главно

  • Weak Reference — референца која не утиче на retain count објекта; при ослобађању објекта се поништава
  • Декларација — кључна реч weak испред var; тип увек опционалан (?)
  • Примена — делегати, затварања, parent-child односи за прекидање retain cycle-а
  • Безбедност — аутоматско постављање на nil након деалокације објекта (zeroing weak)
  • Разлика од unowned — weak се поништава и безбедан је, unowned се не поништава и захтева гаранцију животног века

Шта је Weak Reference?

Weak Reference — је невласничка референца на објекат у ARC-у (Automatic Reference Counting). За разлику од strong-референце која повећава retain count објекта и гарантује његов живот, weak-референца омогућава објекту да се ослободи чак и ако још увек постоје референце на њега. Након ослобађања, weak-референца се аутоматски поставља на nil — ово се назива zeroing weak.

Zeroing weak — кључна карактеристика Swift и Objective-C runtime-а. Када бројач референци објекта достигне нулу и објекат се деалоцира, runtime прегледа све weak-референце на овај објекат (похрањене у посебној weak-табели) и поставља их на nil. Ово гарантује да су приступи ослобођеној меморији (use-after-free) немогући преко weak-референци — било које читање враћа nil.

Према Apple WWDC 2012 Session 406, zeroing weak референце су елиминисале читаву класу crash грешака повезаних са висећим показивачима (dangling pointers), које су биле распрострањене у ручном управљању меморијом (MRR). У MRR-у слабе референце су постојале само као __unsafe_unretained — нису се поништавале, а приступ ослобођеном објекту је водио ка EXC_BAD_ACCESS.

Синтакса weak у Swift-у и Objective-C-у

Размотримо синтаксу декларисања weak-референци у оба језика Apple екосистема. Упркос заједничком runtime-у, синтакса се разликује, али семантика је идентична.

Swift

У Swift-у weak-референце се декларишу са кључном речи weak испред var. Тип мора увек бити опционалан (Type?), јер се референца може поништити у било ком тренутку. Константе (let) не могу бити weak — само променљиве.

swift
class ViewController: UIViewController {
    // weak-особине: само var, само optional
    weak var delegate: ViewControllerDelegate?
    weak var parentView: UIView?

    weak var completionHandler: ((Bool) -> Void)?  // ⚠️ затварања weak не чувају
    // ⬆️ Грешка: weak се може применити само на class-типове, не на closure
}

Важно: weak се примењује само на инстанце класа (class-types), AnyObject и протоколе наслеђене од AnyObject-а. Struct, enum и closure не могу бити weak — они су вредносни типови и не учествују у ARC-у.

Objective-C

У Objective-C-у weak-особине се декларишу кроз атрибут __weak или модификатор weak у property-ју:

objective-c
// Objective-C: weak property
@interface MyViewController : UIViewController
@property (weak, nonatomic) id<MyDelegate> delegate;
@end

// Локална weak-променљива
__weak MyObject *weakRef = someStrongObject;

Objective-C runtime такође пружа zeroing weak, али додатно блокира употребу weak-а са C-структурама и неким Core Foundation објектима. За њих се примењује __unsafe_unretained — без zeroing-а.

Када примењивати слабе референце

Слабе референце — нису универзално решење, већ алат за специфичне сценарије. Коришћење weak-а свуда доводи до прекомерне сложености и погоршава читљивост. Размотримо исправне сценарије примене.

Делегати (Delegate pattern)

Делегат — главни сценариј за weak. Објекат-власник (нпр. UITableView) држи strong-референцу на себе, а делегат (UIViewController) не треба да поседује табелу. Apple SDK гарантује да су сви delegate и dataSource weak. За сопствене протоколе увек користи weak var delegate.

Parent-Child са повратном везом

Када објекат дете треба да референцира родитеља (нпр. ChildViewController за приступ координатору), користи weak-референцу. Родитељ поседује дете (strong), дете посматра родитеља (weak) — retain cycle искључен.

Асинхрона затварања

Capture list [weak self] — стандардни начин избегавања retain cycle-а у затварањима ускладиштеним као особине класе. Ако self може бити ослобођен пре завршетка затварања — weak self је обавезан.

СценаријWeakStrong
Delegate✅ Увек weak❌ Retain cycle
Parent → Child❌ Није потребно (родитељ треба да поседује)✅ Strong
Child → Parent✅ Weak❌ Retain cycle
Асинхрони callback✅ [weak self]❌ Ризик retain cycle-а
Чврста веза (owned)❌ unowned✅ Strong

Опште правило: ако објекат A поседује B (A → B strong), онда B → A треба да буде weak или unowned. Смер strong-референци увек треба да буде од власника ка подређеном.

Weak vs Unowned: поређење и сценарији

И weak и unowned не повећавају retain count, али се разликују у понашању након деалокације објекта. Избор између њих је питање гаранција животног века.

Разлике

Weak: аутоматски се поништава (nil), тип увек optional, захтева unwrap пре употребе. Безбедан — приступ nil-у не изазива crash.

Unowned: не поништава се, тип non-optional. Ако је објекат ослобођен, unowned-референца постаје висећи показивач — приступ њој изазива runtime crash. Unowned претпоставља да објекат живи не краће од стране која га референцира.

Када изабрати weak

Weak бирај ако: објекат може бити деалоциран у било ком тренутку (делегат након затварања екрана), не контролишеш животни век објекта или сумњаш у гаранције. Weak — универзалан безбедан избор.

Када изабрати unowned

Unowned бирај ако: објекат гарантовано не може бити ослобођен пре референцирајућег (нпр. Customer → CreditCard, где картица не постоји без клијента). Unowned даје non-optional API без unwrap-а, што је згодније у коду.

swift
class Order {
    let id: Int
    var items: [Item] = []

    init(id: Int) { self.id = id }

    // Чврста веза: Order поседује Item
    func addItem(name: String) {
        let item = Item(name: name, order: self)
        items.append(item)
    }
}

class Item {
    let name: String
    unowned let order: Order          // ✅ unowned — Item не живи без Order-а

    init(name: String, order: Order) {
        self.name = name
        self.order = order
    }
}

// Пример са weak: делегат без гаранције животног века
protocol NetworkServiceDelegate: AnyObject {
    func didReceiveResponse(data: Data)
}

class NetworkService {
    weak var delegate: NetworkServiceDelegate?  // ✅ weak — делегат може отићи
}

У примеру Item користи unowned, јер елемент поруџбине не може постојати без саме поруџбине — гаранција животног века је гвоздена. NetworkService користи weak, јер делегат (нпр. ViewController) може бити затворен и ослобођен у било ком тренутку.

Ограничења weak референци и замке

Слабе референце — моћан алат, али имају ограничења која је важно разумети за правилну примену у iOS развоју.

Перформансе weak-а

Слабе референце су спорије од strong: при сваком приступу runtime проверава да ли је објекат ослобођен (lookup у weak-табели). У огромној већини сценарија разлика је неприметна, али у врућим петљама са милионима приступа weak може бити уско грло. За сценарије са високим оптерећењем користи strong и реорганизуј архитектуру.

Weak се не примењује на вредносне типове

Struct, enum, tuple — вредносни типови који не учествују у ARC-у. Покушај декларисања weak struct-а доводи до грешке компајлирања. За чување слабе референце на вредносни тип користи омотач у class-типу или затварање.

Weak у више niti

Zeroing weak је thread-safe: ако се објекат ослобађа на једној нити, weak-референца се поништава на свим нитима атомски. Међутим, размак између читања weak-референце и њеног коришћења може довести до трке — објекат се ослобађа између добијања weak-референце и њене употребе. Решење: strong-захватање слабе референце у локалну променљиву.

swift
// Race condition са weak у више нити
func performAsync() {
    weak var weakSelf = self
    queue.async {
        // ⚠️ weakSelf може бити nil између провере и употребе
        if weakSelf != nil {
            weakSelf!.doSomething()  // CRASH ако је постао nil
        }
    }
}

// ✅ Исправка: снажно хватање за време употребе
func performAsyncSafe() {
    queue.async { [weak self] in
        guard let strongSelf = self else { return }
        strongSelf.doSomething()  // strongSelf — локална strong-референца
    }
}

У безбедној варијанти weak self се захвата, затим одмах развија у локалну strong-променљиву strongSelf. Ако је self још увек жив — остаће жив током извршавања блока. Ако није — guard се активира и код се не извршава. Овај идиом — стандардни образац за асинхрона затварања у Swift-у.

UIView и weak outlet

IBOutlet у Interface Builder-у треба да буду weak, јер хијерархија приказа већ држи strong-референцу на subview. Дуплирање strong-референце у контролеру не ствара retain cycle, али је сувишно. Слаба референца на outlet — препорука Apple-а, иако многи програмери користе strong ради поједностављења кода.

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

Може ли weak-референца да показује на објекат који још није креиран?

Не, weak може показивати само на постојећи објекат или nil. При креирању новог објекта прво добијаш strong-референцу (кроз иницијализатор), и тек онда можеш доделити слабу референцу. weak nil на почетку — нормално стање.

Зашто weak ради само са class-типовима?

Weak се заснива на ARC-у који управља само референцним типовима (класе). Вредносни типови (struct, enum) се копирају при додели и немају retain count. За слабу везу вредносних типова користи затварања или омотаче у class-у са weak-особином.

Како weak утиче на перформансе у петљи?

Сваки приступ weak-референци извршава lookup у runtime-табели. У петљи са милионима итерација ово може бити 2–5 пута спорије од strong-референце. За вруће путање копирај weak у локалну strong-променљиву пре петље.

Када weak-референца може неочекивано постати nil?

Када су све strong-референце на објекат изгубљене — на крају опсега, при поновном постављању особине, при затварању екрана. У више niti окружењу ово може наступити између две линије кода. Увек проверавај weak кроз guard let или if let.

По чему се weak разликује од __weak у Objective-C-у?

Семантички идентично: оба пружају zeroing weak. Разлике: Swift захтева optional-тип и var, Objective-C користи модификатор property-ја. Objective-C такође подржава __unsafe_unretained — слабу референцу без zeroing-а (ризик висећег показивача).

Резиме

  • Weak Reference — невласничка референца која не повећава retain count и аутоматски се поништава при деалокацији
  • Синтаксаweak var + optional тип; само class-типови и AnyObject протоколи
  • Zeroing weak — runtime поништава све weak-референце на ослобођени објекат, спречавајући висеће показиваче
  • Сценарији — делегати, parent-child са повратном везом, асинхрона затварања ([weak self])
  • Weak vs Unowned — weak се поништава (безбедно), unowned се не поништава (ризик crash-а, али non-optional)
  • Перформансе — weak спорији од strong због lookup-а у runtime-табели; за вруће путање копирај у strong
  • Препорука — ако ниси сигуран у гаранције животног века — бирај weak

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

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

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

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