Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Аналоги weak в Swift
Что такое weak
weak — это модификатор ссылки, который не увеличивает счетчик ссылок (reference count) объекта. Используется для избежания retain cycles.
class Dog {
weak var owner: Person? // Слабая ссылка
}
class Person {
var dog: Dog? // Сильная ссылка
}
// Без weak был бы retain cycle:
// Person → retains Dog
// Dog → retains Person
// Никто не может быть deallocирован
Основные аналоги и альтернативы
1. unowned — сильно связанный аналог
// weak vs unowned
// weak: может стать nil
class Parent {
var child: Child?
}
class Child {
weak var parent: Parent? // Может быть nil
func greet() {
print(parent?.name ?? "Unknown") // Проверяем nil
}
}
// unowned: никогда не nil (гарантия программиста)
class Parent {
var child: Child?
}
class Child {
unowned var parent: Parent // НИКОГДА не nil, иначе crash
func greet() {
print(parent.name) // Прямой доступ, parent существует всегда
}
}
// Использование:
let parent = Parent()
let child = Child(parent: parent) // parent существует при создании
parent.child = child // Теперь связаны
// parent не может быть deallocирован, пока child жив
// Но child может быть deallocирован без parent
2. unowned(unsafe) — самый опасный аналог
// unowned(unsafe): не проверяет, что объект существует
class Something {
unowned(unsafe) var reference: AnyObject // Опасно!
}
// Используется только когда:
// 1. Абсолютно уверен, что объект существует
// 2. Нужна максимальная производительность
// 3. Согласовано с командой на жизнь и смерть
// Пример неправильного использования:
var obj: AnyObject? = SomeObject()
var unsafe = obj // unowned(unsafe)
obj = nil // Deallocируется
// Теперь unsafe указывает на освобожденную память
print(unsafe) // Undefined behavior!
3. Closure captures: [weak self]
// Самый частый случай использования weak
class ViewController {
func loadData() {
// ❌ Проблема: closure захватывает self сильной ссылкой
api.fetch { data in
self.updateUI(with: data) // Retain cycle!
// closure → self
// self.completionHandler → closure
}
}
// ✅ Решение: weak self
func loadDataSafely() {
api.fetch { [weak self] data in
guard let self = self else { return }
self.updateUI(with: data) // Безопасно
}
}
// ✅ Альтернатива: unowned self (если VC жив пока делается запрос)
func loadDataFast() {
api.fetch { [unowned self] data in
self.updateUI(with: data) // Быстрее, но гарантия
}
}
}
4. Capture lists в других контекстах
// В Combine
var cancellables = Set<AnyCancellable>()
button.publisher
.sink { [weak self] _ in
self?.handleTap()
}
.store(in: &cancellables)
// В DispatchQueue
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
self?.updateUI()
}
// В Alamofire
AF.request(url).responseJSON { [weak self] response in
self?.handleResponse(response)
}
5. [unowned self] — в обычных closures
class DataManager {
var onDataUpdate: (() -> Void)?
func startUpdating() {
// Если closure вызывается всегда когда self живой
self.onDataUpdate = { [unowned self] in
self.processData()
}
}
// Проблема: если closure вызовется после dealloc
func dangerousExample() {
var callback: (() -> Void)?
let dataManager = DataManager()
callback = { [unowned dataManager] in
dataManager.processData() // Crash если dataManager deallocирован
}
// dataManager deallocируется
// callback?.call() // CRASH!
}
}
6. Strong Weak Dance — в delegates
protocol DataDelegate: AnyObject {
func didUpdateData(_ data: Data)
}
class DataFetcher {
weak var delegate: DataDelegate? // Слабая ссылка
func fetchData() {
// delegate может быть nil
guard let delegate = self.delegate else { return }
// Или:
delegate?.didUpdateData(data)
}
}
class ViewController: DataDelegate {
let fetcher = DataFetcher()
override func viewDidLoad() {
super.viewDidLoad()
fetcher.delegate = self // ViewController не retain'ится
}
func didUpdateData(_ data: Data) {
// Обновляем UI
}
}
Таблица сравнения
┌──────────┬──────────────────┬──────────────┬─────────────┐
│ Тип │ Увеличивает RC │ Может быть nil│ Crash если nil│
├──────────┼──────────────────┼──────────────┼─────────────┤
│ strong │ Да │ Нет (обычно) │ Нет │
│ weak │ Нет │ Да │ Нет │
│ unowned │ Нет │ Нет │ Да │
│ unowned │ Нет │ Нет │ Да (UB) │
│ (unsafe) │ │ │ │
└──────────┴──────────────────┴──────────────┴─────────────┘
Когда использовать что
// 1. Parent → Child: используй strong
class Parent {
var children: [Child] = [] // Strong
}
// 2. Child → Parent: используй weak
class Child {
weak var parent: Parent? // Weak
}
// 3. Optional relationships: используй weak
class User {
weak var friend: User? // Может быть nil
}
// 4. Guaranteed lifetime: используй unowned
class CreditCard {
unowned var owner: Person // Owner всегда существует
}
// 5. Closures в self: используй [weak self]
self.handler = { [weak self] in
self?.process()
}
// 6. Delegates: используй weak
class Manager {
weak var delegate: ManagerDelegate?
}
Практические примеры
MVVM без retain cycles
class ViewController: UIViewController {
@IBOutlet weak var nameLabel: UILabel!
let viewModel = UserViewModel()
override func viewDidLoad() {
super.viewDidLoad()
// ✅ Правильно: weak self в binding
viewModel.$userName
.sink { [weak self] name in
self?.nameLabel.text = name
}
.store(in: &cancellables)
}
}
class UserViewModel: ObservableObject {
@Published var userName: String = ""
init() {
// Не захватываем self
Task {
let user = await fetchUser()
self.userName = user.name
}
}
}
Network requests
class APIClient {
func fetchUser(_ id: String, completion: @escaping (User) -> Void) {
// weak self не нужен, если closure не retainится долго
URLSession.shared.dataTask(with: url) { data, _, _ in
let user = parse(data)
completion(user) // completion вызовется сразу
}.resume()
}
func fetchUserSafely(
_ id: String,
completion: @escaping (User) -> Void
) {
URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
guard let self = self else { return } // Если APIClient deallocирован
let user = self.parse(data)
completion(user)
}.resume()
}
}
Avoiding multiple captures
// ❌ Плохо: множественные захваты
class Controller {
func setup() {
button.setTitle(title, for: .normal)
button.setOnTap { [weak self] in
self?.handleTap1()
}
}
}
// ✅ Хорошо: одна точка входа
class Controller {
func setup() {
bind(to: button)
}
private func bind(to button: UIButton) {
button.setOnTap { [weak self] in
self?.handleTap()
}
}
}
Инструменты для поиска retain cycles
// 1. Instruments → Memory Graph
// Показывает все references и их типы
// 2. Debug memory graph (в Xcode)
// При pause выполнения: Debug → View Memory Graph
// 3. Swiftlint rule
// swiftlint: disable weak_delegate
Выводы
- weak — для relationships где parent держит reference
- unowned — когда 100% уверен в lifetime
- [weak self] — стандарт в closures
- unowned(unsafe) — только для performance-critical code
- Избегай retain cycles — источник утечек памяти
- Используй Instruments — для поиска проблем