Memory management in Swift is based on automatic reference counting (ARC), which means that an object exists in memory as long as there is at least one strong reference to it. After that, ARC initiates the object deallocation mechanism, depending on the number of existing weak and unowned object references, the deallocation mechanism will be different. However, in addition to ARC, Swift also supports manual memory management. In this article, I will tell you what are the ways to work with memory that provide create/read/update/delete (CRUD) operations and much more. 1. Pointers Manual memory management can be implemented based on pointers. Pointer types vary depending on the need for unsafe memory access. The data type is: Pointers to a piece of memory without an explicit type. Returns the number of bytes. Usually contains in the name; Raw Pointers with an explicit type are specified during initialization as a generic parameter. contain in the name; Does not Raw Variability is distinguished by: Pointers to a memory area with the possibility of changing it. The name contains ; Mutable Pointers to a piece of memory without the possibility of changing it. The name contain ; does not Mutable The number of elements is distinguished by: Pointers that operate on an array of elements. The name contains ; Buffer Pointers that operate on a single element. The name contain ; does not Buffer In total, all possible combinations of pointers look like this: ; UnsafePointer<T> ; UnsafeMutablePointer<T> ; UnsafeBufferPointer<T> ; UnsafeMutableBufferPointer<T> ; UnsafeRawPointer ; UnsafeMutableRawPointer ; UnsafeRawBufferPointer ; UnsafeMutableRawBufferPointer 2. Creating objects Let’s consider the creation of objects in the example of UnsafePointer. var x: Int = 10 let unsafePointer = UnsafePointer<Int>(&x) Because is an immutable pointer, it can only be initialized by passing it an already initialized object directly. UnsafePointer You can get information about the memory area stored at a given pointer as follows: unsafePointer.pointee // printed 10 Consider the creation of objects in the example of . Unlike , this pointer can be initialized before information is written to the memory area. UnsafeMutablePointer UnsafePointer let size = MemoryLayout<Int>.size let unsafeMutablePointer = UnsafeMutablePointer<Int>.allocate(capacity: size) unsafeMutablePointer.pointee = 5 Now, through the pointee property, you can read and write the allocated memory area. Since we are working with the data type, the capacity area was chosen taking into account the required size of the . Int MemoryLayout You can deallocate a memory area and a pointer to it as follows: unsafeMutablePointer.deallocate() unsafeMutablePointer.deinitialize(count: 1) Consider the creation of elements in the example of . Since this pointer is mutable and without explicit typing, it is enough to allocate a memory area for an object and then write data to it. In this case, all operations for this pointer occur byte by byte without a specific data type. UnsafeMutableRawPointer let unsafeMutableRawPointer = UnsafeMutableRawPointer.allocate(byteCount: size, alignment: alignment) // 6000006E44F0 unsafeMutableRawPointer.storeBytes(of: 32, as: Int.self) // 6000006E44F0 unsafeMutableRawPointer.load(as: Int.self) // printed 32 The method allows you to read a memory area with a given data type. The method allows you to write to the allocated memory area. At the same time, because of the lack of binding to a specific data type, you can easily put and read data with a completely different type into the allocated area: load storeBytes unsafeMutableRawPointer.initializeMemory(as: String.self, to: "123") // 6000006E44F0 unsafeMutableRawPointer.load(as: String.self) // printed “123” unsafeMutableRawPointer.deallocate() The address was the number 32 with the data type , but we rewrote it to the string , hello Python. 6000006E44F0 Int "123" 3. Copying In addition to standard CRUD operations, pointers also support copying memory from one address to another. let size = MemoryLayout<Int>.size let alignment = MemoryLayout<Int>.alignment let unsafeMutableRawPointer1 = UnsafeMutableRawPointer.allocate(byteCount: size, alignment: alignment) unsafeMutableRawPointer1.storeBytes(of: 32, as: Int.self) // 32 let unsafeMutableRawPointer2 = UnsafeMutableRawPointer.allocate(byteCount: size, alignment: alignment) unsafeMutableRawPointer2.storeBytes(of: 40, as: Int.self) // 40 unsafeMutableRawPointer2.copyMemory(from: unsafeMutableRawPointer1, byteCount: size) unsafeMutableRawPointer2.load(as: Int.self) // 32 Two pointers were created for different memory locations containing the numbers 32 and 40. Thanks to , we were able to copy information from one memory location to another. At the same time, the use of the method allows one-time copying, preserving the further independence of memory sections with different addresses. copyMemory copyMemory 4. Binding It is also possible to bind two different pointers: let unsafeMutableRawPointer3 = UnsafeMutableRawPointer.allocate(byteCount: size, alignment: alignment) unsafeMutableRawPointer3.storeBytes(of: 32, as: Int.self) let unsafeMutableRawPointer4 = unsafeMutableRawPointer3.bindMemory(to: Int.self, capacity: size) unsafeMutableRawPointer4.pointee // 32 unsafeMutableRawPointer3.storeBytes(of: 40, as: Int.self) unsafeMutableRawPointer4.pointee // 40 Thanks to , both pointers point to the same memory location and will catch all changes, regardless of which pointer they are written to. bindMemory 5. Collections To allocate an area of memory for an array using , the area for each element of the array will be allocated first: UnsafeMutableBufferPointer let array: [Int] = [5, 6, 7, 8, 9] let elementPointer = UnsafeMutablePointer<Int>.allocate(capacity: array.count) let arrayPointer = UnsafeMutableBufferPointer(start: elementPointer, count: array.count) Let’s fill in the previously allocated area. The offset between memory locations of different elements will be provided by the method. advanced(by: Int) for (index, value) in array.enumerated() { elementPointer.advanced(by: index).pointee = value } In addition to writing, method also helps when reading a specific array element by ordinal index: the advanced(by: Int) elementPointer.advanced(by: 4).pointee // 9 supports to read and write array elements by ordinal index: UnsafeMutableBufferPointer subscript[index] arrayPointer[4] = 5 elementPointer.advanced(by: 4).pointee // 5 arrayPointer.deallocate() On this, the article comes to an end. I will talk about other types of manual memory management, such as , in the next article. Unmanaged objects Don’t hesitate to contact me on if you have any questions. Also, you can always . Twitter buy me a coffee Also published . here