← Назад к вопросам

Как устроена память в iOS?

2.7 Senior🔥 61 комментариев
#Управление памятью

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как устроена память в iOS?

Память в iOS строится на уровне процесса. Каждый iOS процесс получает собственное виртуальное адресное пространство, изолированное от других процессов благодаря MMU (Memory Management Unit).

Виртуальное адресное пространство

0xFFFFFFFF ┌──────────────────────────┐
           │   Kernel Space           │ (зарезервирована для системы)
           │   (недоступна приложению)│
           ├──────────────────────────┤
           │   Stack (растёт вниз ↓)  │ (локальные переменные, параметры)
           │                          │
           │          ↓               │
           │                          │
           ├──────────────────────────┤
           │    Unused Memory         │
           │                          │
           │          ↑               │
           │                          │
           │   Heap (растёт вверх ↑) │ (динамические объекты, массивы)
           ├──────────────────────────┤
           │   .bss (неинициал. данные)│
           │   .data (инициализированные данные)
           │   .text (код, константы) │
           │                          │
0x00000000 └──────────────────────────┘

Главные компоненты

1. Stack (Стек)

Быстрая память для локальных переменных и параметров функций:

func example() {
    let x = 10              // Stack
    let y = 20              // Stack
    var point = Point(x: 0, y: 0)  // Stack (if struct)
}  // ← автоматически удаляется

Характеристики:

  • Линейное выделение (просто сдвигаем указатель)
  • Автоматическое освобождение при выходе из scope
  • Ограниченный размер (на iPhone ~1 MB)
  • Очень быстрый доступ
  • LIFO (Last In First Out)
func stackExample() {
    let a = 1        // Stack pointer +4 bytes
    let b = 2        // Stack pointer +4 bytes
    let c = 3        // Stack pointer +4 bytes
    // функция заканчивается
    // Stack pointer -12 bytes (выход из всех трёх переменных)
}

2. Heap (Куча)

Динамическая память для объектов, которые живут дольше, чем функция:

class User {              // Heap
    let name: String     // String данные в Heap
    let age: Int
}

func example() {
    let user = User(name: "John", age: 30)
    // переменная user на Stack
    // объект User в Heap
    // переменная user содержит адрес объекта
}
// пользователь удаляется благодаря ARC

Характеристики:

  • Выделение произвольного размера
  • Требует управления (ARC в Swift)
  • Медленнее, чем Stack
  • Большой размер (~GB на современных iPhone)
  • Фрагментируется со временем

Система управления памятью: ARC

ARC (Automatic Reference Counting) подсчитывает ссылки на объекты в Heap:

class Person {}

var p1 = Person()    // refCount = 1
var p2 = p1          // refCount = 2
p1 = nil             // refCount = 1
p2 = nil             // refCount = 0 → объект удаляется из Heap

Retain cycles (утечки памяти)

class Parent {
    var child: Child?    // сильная ссылка
}

class Child {
    var parent: Parent?  // сильная ссылка
}

var p = Parent()
var c = Child()
p.child = c          // Parent → Child
c.parent = p         // Child → Parent

p = nil  // refCount(Parent) = 1 (указывает Child)
c = nil  // refCount(Child) = 1 (указывает Parent)
// УТЕЧКА ПАМЯТИ: оба объекта никогда не удалятся!

Решение: weak reference

class Parent {
    var child: Child?
}

class Child {
    weak var parent: Parent?  // слабая ссылка!
}

var p = Parent()
var c = Child()
p.child = c
c.parent = p

p = nil  // Parent удаляется (refCount = 0)
c = nil  // Child удаляется (refCount = 0)

Детальная архитектура памяти

Text Segment (.text)

let PI = 3.14159     // константа в .text

func printHello() {  // код функции в .text
    print("Hello")
}

// .text — read-only, не может быть изменён

Data Segment (.data, .bss)

var globalCounter = 0  // .data (инициализирована нулём)
var staticArray: [Int] = []  // .data
// .bss используется для неинициализированных данных

Registers (регистры CPU)

Наиболее быстрая память — регистры процессора:

// Компилятор может поместить в регистр
let x = 1 + 2        // может быть в регистре
let y = x * 2        // может быть в регистре

Виды памяти и их скорость

Регистры:       ████ наносекунды (1-2 цикла)
L1 Cache:       ███ наносекунды (2-4 цикла)
L2 Cache:       ██ наносекунды (10 циклов)
Stack:          ██ наносекунды (50 циклов)
Heap:           ░ микросекунды (100+ циклов)
Disk/Network:   ░░░ миллисекунды (1,000,000+ циклов)

Управление памятью в Swift

Value Types (на Stack)

struct Point {       // Value Type
    let x: Int       // Stack
    let y: Int       // Stack
}

func test() {
    let p = Point(x: 0, y: 0)  // весь Point на Stack
}  // p удаляется со Stack

Reference Types (на Heap)

class Location {     // Reference Type
    let x: Int       // Heap
    let y: Int       // Heap
}

func test() {
    let loc = Location(x: 0, y: 0)  // объект в Heap
}  // loc переменная исчезает, объект удаляется через ARC

Memory Footprint

var array = [1, 2, 3]       // переменная на Stack, массив в Heap
print(MemoryLayout<[Int]>.size)  // 16 или 24 байт на Stack

var string = "Hello"        // переменная на Stack, данные в Heap
print(MemoryLayout<String>.size)  // 24 байт на Stack

var number = 42             // полностью на Stack
print(MemoryLayout<Int>.size)  // 8 байт на Stack

Утечки памяти: как найти

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // ❌ УТЕЧКА: closure захватывает self strongly
        DispatchQueue.main.asyncAfter(deadline: .now() + 100) {
            print(self.title)  // self не может быть удалён
        }
    }
}

// ✅ ПРАВИЛЬНО: используй [weak self]
DispatchQueue.main.asyncAfter(deadline: .now() + 100) { [weak self] in
    print(self?.title)
}

Практические советы

1. Prefer Stack over Heap

// ✅ ЛУЧШЕ: struct (обычно на Stack)
struct Point {
    let x: Int
    let y: Int
}

// ❌ ХУЖЕ: class (на Heap, ARC overhead)
class Point {
    let x: Int
    let y: Int
}

2. Избегай retain cycles

// ✅ ПРАВИЛЬНО
class ViewController: UIViewController {
    var completionHandler: (() -> Void)?
    
    func setup() {
        completionHandler = { [weak self] in
            self?.doSomething()
        }
    }
}

3. Профилируй память

// Xcode → Debug → Memory Graph
// Инструменты: Instruments → Allocations, Leaks

Вывод

Память в iOS разделена на:

  • Stack — быстро, ограничено, автоматическое управление
  • Heap — медленнее, большой размер, требует ARC
  • Registers — самые быстрые, управляются компилятором

Rust правило: Move stack allocation by default, only use heap when needed. Swift следует этому с Value vs Reference Types.

Как устроена память в iOS? | PrepBro