Weak Reference — co to je, syntaxe a použití v mobilním vývoji

Autor: IT Sectr Publikováno: 2026-03-30 Doba čtení: 9 min

Weak Reference (slabá reference) — je odkaz na objekt, který nezvyšuje jeho počítadlo držení v ARC. Podle Apple Swift Language Guide, 2026 se slabé reference deklarují klíčovým slovem weak a mají vždy volitelný typ. Když je objekt uvolněn, všechny slabé reference na něj jsou automaticky nastaveny na nil, což zabraňuje visícím ukazatelům a činí slabé reference bezpečným mechanismem pro přerušení retain cycle.

Hlavní

  • Weak Reference — reference neovlivňující retain count objektu; při uvolnění objektu je vynulována
  • Deklarace — klíčové slovo weak před var; typ vždy volitelný (?)
  • Použití — delegáti, uzávěry, parent-child vztahy pro přerušení retain cycle
  • Bezpečnost — automatické nastavení na nil po dealokaci objektu (zeroing weak)
  • Rozdíl od unowned — weak se vynuluje a je bezpečný, unowned se nevynuluje a vyžaduje záruku životnosti

Co je Weak Reference?

Weak Reference — je nevlastnící reference na objekt v ARC (Automatic Reference Counting). Na rozdíl od strong reference, která zvyšuje retain count objektu a zaručuje jeho život, slabá reference umožňuje objektu být uvolněn, i když na něj stále existují reference. Po uvolnění je slabá reference automaticky nastavena na nil — tomu se říká zeroing weak.

Zeroing weak — klíčová vlastnost runtime Swift a Objective-C. Když počítadlo referencí objektu dosáhne nuly a objekt je dealokován, runtime projde všechny slabé reference na tento objekt (uložené ve speciální weak tabulce) a nastaví je na nil. To zaručuje, že přístup k uvolněné paměti (use-after-free) je nemožný přes slabé reference — jakékoli čtení vrací nil.

Podle Apple WWDC 2012 Session 406 zeroing weak reference eliminovaly celou třídu crash chyb spojených s visícími ukazateli (dangling pointers), které byly rozšířené v ručním řízení paměti (MRR). V MRR existovaly slabé reference pouze jako __unsafe_unretained — nevynulovaly se a přístup k uvolněnému objektu vedl k EXC_BAD_ACCESS.

Syntaxe weak ve Swift a Objective-C

Podívejme se na syntaxi deklarace slabých referencí v obou jazycích ekosystému Apple. Navzdory společnému runtime se syntaxe liší, ale sémantika je identická.

Swift

Ve Swift se slabé reference deklarují klíčovým slovem weak před var. Typ musí být vždy volitelný (Type?), protože reference může být vynulována kdykoli. Konstanty (let) nemohou být weak — pouze proměnné.

swift
class ViewController: UIViewController {
    // weak-vlastnosti: pouze var, pouze optional
    weak var delegate: ViewControllerDelegate?
    weak var parentView: UIView?

    weak var completionHandler: ((Bool) -> Void)?  // ⚠️ closures weak neukládají
    // ⬆️ Chyba: weak lze aplikovat pouze na class-types, ne na closure
}

Důležité: weak je použitelný pouze na instance tříd (class-types), AnyObject a protokoly zděděné z AnyObject. Struct, enum a closure nemohou být weak — jsou to hodnotové typy a neúčastní se ARC.

Objective-C

V Objective-C se slabé vlastnosti deklarují pomocí atributu __weak nebo modifikátoru weak v property:

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

// Lokální weak-proměnná
__weak MyObject *weakRef = someStrongObject;

Objective-C runtime také poskytuje zeroing weak, ale navíc blokuje použití weak s C-strukturami a některými Core Foundation objekty. Pro ně se používá __unsafe_unretained — bez zeroing.

Kdy používat slabé reference

Slabé reference — nejsou univerzální řešení, ale nástroj pro konkrétní scénáře. Používání weak všude vede k nadměrné složitosti a zhoršuje čitelnost. Podívejme se na správné scénáře použití.

Delegáti (Delegate pattern)

Delegát — hlavní scénář pro weak. Objekt-vlastník (např. UITableView) drží strong referenci na sebe, a delegát (UIViewController) by neměl vlastnit tabulku. Apple SDK zaručuje, že všichni delegate a dataSource jsou weak. Pro vlastní protokoly vždy používejte weak var delegate.

Parent-Child se zpětnou vazbou

Když dítě objekt potřebuje odkazovat na rodiče (např. ChildViewController pro přístup ke koordinátorovi), použijte weak referenci. Rodič vlastní dítě (strong), dítě pozoruje rodiče (weak) — retain cycle vyloučen.

Asynchronní uzávěry

Capture list [weak self] — standardní způsob, jak se vyhnout retain cycle v uzávěrech uložených jako vlastnosti třídy. Pokud self může být uvolněno před dokončením uzávěru — weak self je povinný.

ScénářWeakStrong
Delegate✅ Vždy weak❌ Retain cycle
Parent → Child❌ Není třeba (rodič by měl vlastnit)✅ Strong
Child → Parent✅ Weak❌ Retain cycle
Asynchronní callback✅ [weak self]❌ Riziko retain cycle
Silné propojení (owned)❌ unowned✅ Strong

Obecné pravidlo: pokud objekt A vlastní B (A → B strong), pak B → A by měl být weak nebo unowned. Směr strong referencí by měl být vždy od vlastníka k podřízenému.

Weak vs Unowned: srovnání a scénáře

Weak i unowned nezvyšují retain count, ale liší se chováním po dealokaci objektu. Volba mezi nimi je otázkou záruk životnosti.

Rozdíly

Weak: automaticky se vynuluje (nil), typ vždy optional, vyžaduje unwrap před použitím. Bezpečný — přístup k nil nezpůsobí crash.

Unowned: nevynuluje se, typ non-optional. Pokud je objekt uvolněn, unowned reference se stane visícím ukazatelem — přístup k ní způsobí runtime crash. Unowned předpokládá, že objekt žije nejméně tak dlouho jako odkazující strana.

Kdy zvolit weak

Weak zvolte, pokud: objekt může být dealokován kdykoli (delegát po zavření obrazovky), neřídíte životnost objektu nebo pochybujete o zárukách. Weak — univerzální bezpečná volba.

Kdy zvolit unowned

Unowned zvolte, pokud: objekt garantovaně nemůže být uvolněn dříve než odkazující (např. Customer → CreditCard, kde karta neexistuje bez zákazníka). Unowned poskytuje non-optional API bez unwrap, což je v kódu pohodlnější.

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

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

    // Silné propojení: Order vlastní 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 nežije bez Order

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

// Příklad s weak: delegát bez záruky životnosti
protocol NetworkServiceDelegate: AnyObject {
    func didReceiveResponse(data: Data)
}

class NetworkService {
    weak var delegate: NetworkServiceDelegate?  // ✅ weak — delegát může odejít
}

V příkladu Item používá unowned, protože položka objednávky nemůže existovat bez samotné objednávky — záruka životnosti je železná. NetworkService používá weak, protože delegát (např. ViewController) může být zavřen a uvolněn kdykoli.

Omezení weak referencí a nástrahy

Slabé reference — mocný nástroj, ale mají omezení, která je důležité pochopit pro správné použití ve vývoji iOS.

Výkon weak

Slabé reference jsou pomalejší než strong: při každém přístupu runtime kontroluje, zda byl objekt uvolněn (lookup ve weak tabulce). V drtivé většině scénářů je rozdíl neznatelný, ale v horkých smyčkách s miliony přístupů může být weak úzkým hrdlem. Pro scénáře s vysokým zatížením používejte strong a reorganizujte architekturu.

Weak se nevztahuje na hodnotové typy

Struct, enum, tuple — hodnotové typy, které se neúčastní ARC. Pokus o deklaraci weak struct vede k chybě kompilace. Pro uložení slabé reference na hodnotový typ použijte wrapper v class-type nebo closure.

Weak ve vícevláknovém prostředí

Zeroing weak je thread-safe: pokud je objekt uvolněn v jednom vlákně, slabá reference je vynulována ve všech vláknech atomicky. Nicméně mezera mezi čtením slabé reference a jejím použitím může vést k závodnímu stavu — objekt je uvolněn mezi získáním slabé reference a jejím použitím. Řešení: strong-zachycení slabé reference do lokální proměnné.

swift
// Závodní stav s weak ve vícevláknovém prostředí
func performAsync() {
    weak var weakSelf = self
    queue.async {
        // ⚠️ weakSelf může být nil mezi kontrolou a použitím
        if weakSelf != nil {
            weakSelf!.doSomething()  // CRASH pokud se stal nil
        }
    }
}

// ✅ Oprava: silné zachycení na dobu použití
func performAsyncSafe() {
    queue.async { [weak self] in
        guard let strongSelf = self else { return }
        strongSelf.doSomething()  // strongSelf — lokální strong reference
    }
}

V bezpečné variantě je weak self zachycen, poté okamžitě rozbalen do lokální strong proměnné strongSelf. Pokud je self stále živý — zůstane živý po dobu provádění bloku. Pokud ne — guard se aktivuje a kód se neprovede. Tento idiom — standardní vzor pro asynchronní uzávěry ve Swift.

UIView a weak outlet

IBOutlet v Interface Builder by měly být weak, protože hierarchie pohledů již drží strong referenci na subview. Duplikování strong reference v kontroleru nevytváří retain cycle, ale je nadbytečné. Slabá reference na outlet — doporučení Apple, i když mnoho vývojářů používá strong pro zjednodušení kódu.

Často kladené otázky

Může slabá reference ukazovat na objekt, který ještě nebyl vytvořen?

Ne, weak může ukazovat pouze na existující objekt nebo nil. Při vytváření nového objektu nejprve získáte strong referenci (přes inicializátor) a teprve poté můžete přiřadit slabou referenci. weak nil na začátku — normální stav.

Proč weak funguje pouze s class-types?

Weak je založen na ARC, který spravuje pouze referenční typy (třídy). Hodnotové typy (struct, enum) se při přiřazení kopírují a nemají retain count. Pro slabé propojení hodnotových typů použijte closure nebo wrapper v class s weak vlastností.

Jak weak ovlivňuje výkon ve smyčce?

Každý přístup k weak referenci provádí lookup v runtime tabulce. Ve smyčce s miliony iterací to může být 2–5krát pomalejší než strong reference. Pro horké cesty zkopírujte weak do lokální strong proměnné před smyčkou.

Kdy se může slabá reference neočekávaně stát nil?

Když jsou všechny strong reference na objekt ztraceny — na konci rozsahu, při resetování vlastnosti, při zavření obrazovky. Ve vícevláknovém prostředí se to může stát mezi dvěma řádky kódu. Vždy kontrolujte weak přes guard let nebo if let.

Čím se weak liší od __weak v Objective-C?

Sémanticky identické: oba poskytují zeroing weak. Rozdíly: Swift vyžaduje optional typ a var, Objective-C používá modifikátor property. Objective-C také podporuje __unsafe_unretained — slabou referenci bez zeroing (riziko visícího ukazatele).

Shrnutí

  • Weak Reference — nevlastnící reference, která nezvyšuje retain count a automaticky se vynuluje při dealokaci
  • Syntaxeweak var + volitelný typ; pouze class-types a protokoly AnyObject
  • Zeroing weak — runtime vynuluje všechny weak reference na uvolněný objekt, zabraňuje visícím ukazatelům
  • Scénáře — delegáti, parent-child se zpětnou vazbou, asynchronní uzávěry ([weak self])
  • Weak vs Unowned — weak se vynuluje (bezpečný), unowned se nevynuluje (riziko crash, ale non-optional)
  • Výkon — weak pomalejší než strong kvůli lookup v runtime tabulce; pro horké cesty zkopírujte do strong
  • Doporučení — pokud si nejste jisti zárukami životnosti — zvolte weak

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také