Списки захвата и 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 selfunownedне является 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 — локальная установка не требуется.
Все уроки этого курса
- Захват значений по ссылке
- Убегающие замыкания
- Списки захвата и weak self
- Автозамыкания