1. Что такое указатель?

Читайте здесь: https://denniskubes.com/2017/01/24/the-5-minute-guide-to-c-pointers/

2. Небезопасная работа в Swift

Большинство операций Swift безопасны, и Optional введение также предназначено для обеспечения безопасности. Компилятор поможет нам выполнить статическую проверку в большинстве случаев, тип Int не может использоваться как String массовое участие, тип Optional должен быть Unwrapped для безопасного использования в дальнейшем.

Но Swift также поддерживает прямые операции с памятью, эти операции небезопасны, поэтому API покрывается префиксом Unsafe, например указателем типа UnsafePointer.

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

let arr = []
let test = arr[1]

Во время компиляции об ошибках не сообщается, сбой во время выполнения.

3. Схема памяти

C / C ++ в общих sizeof() ключевых словах Swift, которые изначально были там, и sizeofValue, strideof, strideofValue, align и alignOf. В Swift 4 это набор функций, завернутых в MemoryLayout эту структуру внутри, это время предложения.

Таким образом, информация, относящаяся к выравниванию памяти и т.п., является памятью, занятой MemoryLayout получением, например:

MemoryLayout<UInt32>.size // 4
MemoryLayout<UInt32>.alignment // 4
MemoryLayout<UInt32>.stride // 4

UInt32 по умолчанию равен 4 байтам, а память выровнена по 4 байтам.

Когда несколько экземпляров непрерывной памяти объединены в массив (например, массив), расстояние от начального адреса предыдущего экземпляра до начального адреса следующего экземпляра составляет 4 байта.

На моей 64-битной машине тип Int совпадает с типом UInt64, который составляет 8 байтов.

Выравнивание памяти

Зачем делать выравнивание памяти? Вот цель: Цель выравнивания памяти - переполнение стека

Теоретически единица физической памяти составляет один байт, а наименьшая память, к которой мы можем получить доступ, - это один байт. Но на самом деле, по причинам эффективности или аппаратным ограничениям, мы обращаемся к памяти всегда, кратной длине слова, длина слова определяется устройством, например 64-битной машиной с 8 байтами в качестве длины слова, поэтому что каждый раз, когда устройство обращается к памяти, 8 байт. Если значение в памяти не находится на 8-байтовой границе, то процессор должен дважды обратиться к памяти, чтобы получить это значение. По соображениям производительности компилятор обычно выполняет выравнивание памяти для этих значений и использует пространство для времени.

Кроме того, адрес памяти, к которому приложение может получить доступ, - это не физический адрес, а виртуальный адрес, доступ к которому осуществляется через операционную систему.

Например, следующее Struct:

struct SampleStruct {
  let number: UInt32
  let flag: Bool
}
 
MemoryLayout<SampleStruct>.size       // returns 5
MemoryLayout<SampleStruct>.alignment  // returns 4
MemoryLayout<SampleStruct>.stride     // returns 8

UInt32Length 4, Bool length 1, общая длина на самом деле равна 5. Однако из-за выравнивания памяти весь Struct в памяти будет выровнен по 4 байтам, что составляет 8 байтов (64-битные машины).

Struct и Int как тип значения (Тип значения), но если это класс ссылочного типа (Тип ссылки), такой как класс, это не то же самое.

Базовая реализация класса Swift на самом деле является классом Objective C (согласно видео Микеша, Xcode 8, Swift 3). Когда мы создаем новый экземпляр класса Swift, мы выделяем больший объем памяти в куче. Используется для хранения такой информации, как type, reference count в стеке, только один указатель выделяется памяти в куче. Итак, для class выполнения MemoryLayout результаты будут следующими:

class SampleClass {
  let number: UInt32
  let flag: Bool
}
 
MemoryLayout<SampleClass>.size       // returns 8
MemoryLayout<SampleClass>.alignment  // returns 8
MemoryLayout<SampleClass>.stride     // returns 8

4. Быстрый указатель

В Swift всего 8 типов указателей. В основе лежит неизменяемый указатель UnsafePointer и соответствующий указатель переменной UnsafeMutablePointer. Также существует массив, который может вставлять Buffer указателя и не знать, каково содержимое Raw указателя, что не указано в дженериках.

Четыре типа указателей:

  • НебезопасныйMutablePointer
  • Небезопасный указатель
  • НебезопасныйMutableBufferPointer
  • НебезопасныйBufferPointer

Четыре типа без типа RawPointer:

  • НебезопасныйMutableRawPointer
  • Небезопасный
  • НебезопасныйMutableRawBufferPointer
  • НебезопасныйRawBufferPointer

В документации Apple Developer есть таблица указателей C и указателей Swift:

C SyntaxSwift SyntaxConst Type * UnsafePointer Type * UnsafeMutablePointer Type * const * UnsafePointer Type * __strong * UnsafeMutablePointer Type ** AutoreleasingUnsafeMutablePointerConst void * UnsafeRawPointerVoid

5. Специальные указатели

Вышеупомянутые восемь видов указателей относительно распространены, например const int * соответствующий Swift UnsafePointer<Int32>. Так что, если некоторые из более сложных требований не удовлетворяются указанными выше указателями?

Непрозрачный указатель

Если тип указателя C не может найти соответствующий тип в Swift, вы можете использовать этот указатель для его выражения, например указатель типа C Struct.

В методе инициализации этого указателя есть специальный метод:

init?(bitPattern: Int)
    // Creates an OpaquePointer from a given address in memory.

Этот указатель может быть инициализирован адресом памяти. Например, теперь Swift To вызывает функцию C, передавая contextpointer функции обратного вызова, когда указатель C обратно на Swift помещает этот параметр, эквивалент общего userinfo ObjC. Мы можем сделать это:


let pointer = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
 

let someObj = Unmanaged<ObjectClass>.fromOpaque(context!).takeUnretainedValue()

6. Резюме

Указатель - очень мощный инструмент, а также очень обширный инструмент на языке C / C ++ / ObjC. Но одновременная работа с памятью напрямую сопряжена с очень высоким риском для программы. Когда проект простой, проблема небольшая. Если это сложно, легко утонуть в коде.

Дизайн Swift требует безопасности. Большинство функций, которые мы обычно используем, не нуждаются в работе с данными в голой памяти. Все указатели, привязки к памяти и т. Д. Обрабатываются инкапсулированными расширенными типами.

Но Swift также предоставляет небезопасный API для работы с памятью. Хотя Swift пытается быть относительно безопасным по сравнению с интерфейсом C, нам все же нужно быть осторожными при использовании этих API.

Если вы хотите делать ошибки во время использования, вы должны сначала понять, что делают эти небезопасные операции. Вы должны понимать указатели, понимать память и понимать различия между этими операциями в C / C ++ / Swift. Презентация @ mikeash Изучение Swift Memory Layout очень хороша, и ее стоит посмотреть.

7. Ссылки