0Pricing
Swift Academy · Урок

Списки захвата и weak self

Избегайте циклов удержания с помощью [weak self].

«Списки захвата и weak self» — бесплатный урок Swift Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 4 уроков всего.

Риск цикла удержания

Когда объект хранит escaping closure, который сильно захватывает self, каждый из них навсегда удерживает другой — это цикл удержания, приводящий к утечке памяти.

Сильный цикл

Closure сильно захватывает self, а self хранит closure — ни один из них не освобождается:

class Owner {
    var onEvent: (() -> Void)?
    func setup() {
        onEvent = { print(self.describe()) }  // strong self -> cycle
    }
    func describe() -> String { "Owner" }
}
let o = Owner(); o.setup()
o.onEvent?()  // "Owner"  (but leaks)

Списки захвата

Список захвата в начале closure { [weak self] in ... } определяет, как хранится каждая захваченная ссылка: с помощью weak или unowned, а не сильной ссылки.

Разрыв цикла с помощью weak self

[weak self] захватывает self как Optional, не удерживая его в памяти:

class Owner {
    var onEvent: (() -> Void)?
    func setup() {
        onEvent = { [weak self] in
            print(self?.describe() ?? "gone")
        }
    }
    func describe() -> String { "Owner" }
}
let o = Owner(); o.setup()
o.onEvent?()  // "Owner"  (no cycle)

weak делает self необязательным

При использовании [weak self] значение self становится типа Self?, поэтому его нужно распаковать:

class Worker {
    var tag = "W"
    lazy var job: () -> Void = { [weak self] in
        guard let self = self else { return }
        print(self.tag)
    }
}
let w = Worker()
w.job()  // "W"

Шаблон guard let self

Свяжите self заново в начале, чтобы остальная часть closure использовала сильную ссылку, не являющуюся Optional:

class Service {
    var name = "S"
    lazy var run: () -> Void = { [weak self] in
        guard let self else { return }
        print(self.name)
        print("still \(self.name)")
    }
}
Service().run()  // "S" / "still S"

weak и unowned

weak даёт Optional и безопасен, если объект может исчезнуть. unowned не является Optional и работает быстрее, но вызывает сбой, если к нему обратиться после освобождения объекта — используйте его только тогда, когда гарантировано, что self будет жить дольше closure.

Использование unowned

Выбирайте unowned, когда closure не может жить дольше self:

class Timer2 {
    var ticks = 0
    lazy var tick: () -> Void = { [unowned self] in
        self.ticks += 1
    }
}
let t = Timer2()
t.tick(); t.tick()
print(t.ticks)  // 2

Захват отдельных свойств

Захватывайте только необходимые данные, а не весь self — иногда это позволяет полностью избежать цикла:

class VM {
    let id = 42
    lazy var printer: () -> Void = { [id] in print(id) }
}
VM().printer()  // 42

Для non-escaping не нужен weak

Синхронные non-escaping closures (например, при преобразовании) не создают циклов, поэтому сильный захват self в них безопасен:

class Calc {
    let factor = 3
    func scale(_ xs: [Int]) -> [Int] {
        xs.map { $0 * self.factor }   // safe, non-escaping
    }
}
print(Calc().scale([1, 2]))  // [3, 6]

Практическое правило

Используйте [weak self] для сохранённых или escaping closures, которые обращаются к self. Выбирайте unowned только при гарантии, что self остаётся в памяти. Для non-escaping closures не используйте списки захвата.

class P {
    var n = "P"
    var stored: (() -> Void)?
    func arm() { stored = { [weak self] in print(self?.n ?? "-") } }
}
let p = P(); p.arm(); p.stored?()  // "P"

Быстрая проверка

Зачем использовать [weak self] в сохранённом escaping closure?

Итоги

Вы узнали о списках захвата:

  • Сильный self в сохранённом escaping closure создаёт цикл удержания (утечку)
  • [weak self] разрывает его; self становится Optional — используйте guard let self
  • unowned не является Optional и работает быстрее, но вызывает сбой, если self больше не существует
  • Для non-escaping closures списки захвата не нужны

Далее: autoclosures.

Часто задаваемые вопросы

Урок «Списки захвата и weak self» бесплатный?

Да — полный текст урока «Списки захвата и weak self» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 4 уроков всего.

Чему я научусь в уроке «Списки захвата и weak self»?

Избегайте циклов удержания с помощью [weak self]. Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Swift Academy?

Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Списки захвата и weak self»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Swift Academy?

Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Захват значений по ссылке
  2. Убегающие замыкания
  3. Списки захвата и weak self
  4. Автозамыкания
← Назад к Swift Academy