Ответ
Вызов deinit и фактическое освобождение памяти — это два разных этапа в жизненном цикле объекта. deinit вызывается, когда счетчик ссылок достигает нуля, но память может быть освобождена позже из-за системных оптимизаций.
Этапы освобождения объекта:
- Счетчик ссылок → 0 — ARC определяет, что объект больше не нужен
- Вызов
deinit— выполняется пользовательская логика очистки - Освобождение памяти — система возвращает память в пул
Почему есть задержка:
-
Autorelease pools — объекты могут быть добавлены в autorelease pool:
func createTemporary() -> NSString { return NSString(string: "temp") // Может быть autoreleased } // deinit вызовется при дренировании pool, не сразу -
Оптимизации рантайма — система может группировать операции освобождения для эффективности
-
Слабые ссылки и side tables — для weak-ссылок объект переходит в "зомби"-состояние:
class MyClass { deinit { print("Deinit called") } }
var strongRef: MyClass? = MyClass() weak var weakRef = strongRef
strongRef = nil // deinit вызывается // Но weakRef еще какое-то время указывает на "зомби"-объект // Память освобождается после обнуления weak-ссылок
4. **Пул памяти (memory pool)** — система может отложить возврат памяти ОС для повторного использования
**Практический пример:**
```swift
class ResourceHolder {
let id: Int
init(id: Int) {
self.id = id
print("Init (id)")
}
deinit {
print("Deinit (id) called")
// Но память может быть освобождена позже
}
}
func test() {
autoreleasepool {
let obj = ResourceHolder(id: 1)
print("Object created")
} // deinit вызовется здесь
print("Pool drained")
// Память может быть освобождена здесь или позже
}
test()
Важно: Не полагайтесь на мгновенное освобождение памяти в deinit. Для критичных ресурсов (файлы, сетевые соединения) используйте явное закрытие до deinit.