Swift Custom Array Implementation Using UnsafeMutablePointer
Tags: swift, customearray, architecture, beginners
Published: 2024-06-09 12:33:58

In this article, we explore a custom array implementation in Swift using UnsafeMutablePointer. This implementation offers insights into manual memory management, dynamic resizing, and conformance to protocols such as CustomDebugStringConvertible and Sequence. The goal is to provide a detailed overview of the internal workings of Swift arrays.

## Overview

The custom array MyArray<T> allows storage and manipulation of elements of any type T. It supports dynamic resizing, appending, insertion, and removal of elements, while ensuring memory safety through proper allocation and deallocation.

## Key Features

* **Dynamic Resizing:** Automatically adjusts the capacity of the array when it reaches its limit.

* **Memory Management:** Uses UnsafeMutablePointer for low-level memory operations.

* **Sequence Conformance:** Implements the Sequence protocol for iteration.

* **Debug Description:** Provides a custom debug description for easier debugging.

## Implementation Details

#### Properties

```
struct MyArray<T> : CustomDebugStringConvertible, Sequence {
    private var capacity: Int
    private var storage: UnsafeMutablePointer<T>
    private var size: Int
    
    var count: Int {
        return size
    }
        
    var isEmpty: Bool {
        return size == 0
    }

```

**capacity:** The maximum number of elements the array can hold without resizing.

**storage:** A pointer to the array's memory storage.

**size:** The current number of elements in the array.

**count:** Returns the number of elements in the array.

**isEmpty:** Checks if the array is empty.


#### Initialiser

```
init(initialCapacity: Int = 2) {
        self.capacity = initialCapacity
        self.size = 0
        self.storage = UnsafeMutablePointer<T>.allocate(capacity: initialCapacity)
    }
```

#### Resizing

```
private mutating func resize() {
        
   if size >= capacity {
            
     /* Double the capacity */
     let newCapacity = capacity * 2
            
     /* Allocating new storage with new capacity */
     let newStorage = UnsafeMutablePointer<T>.allocate(capacity: newCapacity)
            
     /* Copying the existing elements to the new storage */
     for i in 0..<count {
         newStorage[i] = storage[I]
     }
            
     /* Deallocating old storage */
     storage.deallocate()
     storage = newStorage
     capacity = newCapacity
   }
}
```

The resize method doubles the capacity when needed and reallocates memory, copying existing elements to the new storage.

#### Adding Elements

```
public mutating func append(_ item: T) {
   resize()
   storage[size] = item
   size += 1
}
```
The append method adds a new element to the end of the array, resizing if necessary.

#### Inserting Elements

```
public mutating func insert(_ item: T, at index: Int) {
   guard index >= 0 && index <= size else {
       fatalError("Index out of bounds")
   }
   resize()
   for i in stride(from: count, to: index, by: -1) {
       storage[i] = storage[i - 1]
   }
   storage[index] = item
   size += 1
}

```

The insert method inserts an element at a specified index, shifting elements as needed.

#### Removing Elements

```
@discardableResult
public mutating func remove(at index: Int) -> T {
   guard index >= 0 && index < size else {
       fatalError("Index out of bounds")
   }
   let removedElement = storage[index]
   for i in index..<size - 1 {
      storage[i] = storage[i + 1]
   }
   size -= 1
   return removedElement
}
```
The remove method removes an element at a specified index and returns it.

#### Clearing the Array

```
public mutating func removeAll() {
    // Deallocate the existing elements
    storage.deallocate()
    capacity = 2
    // Reinitialise the storage
    storage = UnsafeMutablePointer<T>.allocate(capacity: capacity)
        size = 0
}
```
The removeAll method deallocates all elements and resets the array.

#### Subscript

```
subscript(index: Int) -> T {
   get {
       guard index >= 0 && index < size else {
           fatalError("Index out of bounds")
       }
       return storage[index]
   }
   set {
       guard index >= 0 && index < size else {
           fatalError("Index out of bounds")
       }
       storage[index] = newValue
   }
}
```
The subscript allows getting and setting elements at a specified index.

#### Sequence Conformance

```
func makeIterator() -> AnyIterator<T> {
   var index = 0
   return AnyIterator {
       guard index < self.size else {
           return nil
       }
       let element = self.storage[index]
       index += 1
       return element
   }
}
```

The makeIterator method provides an iterator for the array.

#### Debug Description

```
var debugDescription: String {
   var result = "["
   for i in 0..<size {
      result += "\(storage[I])"
      if i < size - 1 {
        result += ", "
      }
   }
   result += "]"
   return result
}
```

The debugDescription property returns a string representation of the array.

## Conclusion

This custom array implementation demonstrates how to manage memory manually and provides basic array functionalities. While UnsafeMutablePointer offers powerful capabilities, it requires careful handling to avoid memory leaks and ensure safety. This implementation serves as an educational example and can be extended for more advanced use cases.

Please find the complete source code [Here](https://github.com/benoy/MyArray)