Swift - Memory Safety
Published: 2022-04-07 12:02:56

- Swift makes sure that multiple accesses to the same area of memory don’t conflict, by requiring code that modifies a location in memory to have exclusive access to that memory. 

- A conflicting access to memory can occur when different parts of your code are trying to access the same location in memory at the same time, which can produce unpredictable or inconsistent behavior.

- If you have conflicting access to memory from within a single thread, Swift guarantees that you’ll get an error at either compile time or runtime. For multithreaded code, use Thread Sanitizer to help detect conflicting access across threads.

- A **conflict** can occur if you have two accesses that meet all of the following conditions.

  1. At least one is a write access or a nonatomic access.
  2. They access the same location in memory.
  3. Their durations overlap.


- A **write** access changes the location in memory, but a **read** access doesn’t. 


- The duration of a memory access is either **instantaneous** or **long-term**.


- An access is **instantaneous** if it’s not possible for other code to run after that access starts but before it ends.


- Two instantaneous accesses can’t happen at the same time. Most memory access is instantaneous.


- It’s possible for other code to run after a **long-term** access starts but before it ends, which is called **overlap**. A long-term access can overlap with other long-term accesses and instantaneous accesses.


- Overlapping accesses appear primarily in code that uses **in-out parameters** in functions and methods or **mutating methods** of a structure. 

---
### Conflicting Access to In-Out Parameters

- A function has long-term write access to all of its in-out parameters. 
- The write access for an in-out parameter starts after all of the non-in-out parameters have been evaluated and lasts for the entire duration of that function call. 
- If there are multiple in-out parameters, the write accesses start in the same order as the parameters appear.

- Original variable that was passed as in-out can’t be accessed.

example :

```swift
var stepSize = 1

func increment(_ number: inout Int) {
    number += stepSize
}

increment(&stepSize)
```
Both number and stepSize refer to the same location in memory. The read access to stepSize overlaps with the write access to number, producing a conflict.

The conflict can be solved by :

``` swift
// Make an explicit copy.
var copyOfStepSize = stepSize
increment(&copyOfStepSize)

// Update the original.
stepSize = copyOfStepSize
// stepSize is now 2
```

Another consequence of long-term write access to in-out parameters is that passing a single variable as the argument for multiple in-out parameters of the same function produces a conflict.

example :

```swift
func balance(_ x: inout Int, _ y: inout Int) {
    let sum = x + y
    x = sum / 2
    y = sum - x
}
var playerOneScore = 42
var playerTwoScore = 30
balance(&playerOneScore, &playerTwoScore)  // OK
balance(&playerOneScore, &playerOneScore) // Error: conflicting accesses to playerOneScore
```

passing playerOneScore as the value for both parameters produces a conflict because it tries to perform two write accesses to the same location in memory at the same time.

---

### Conflicting Access to self in Methods
- A mutating method on a structure has write access to self for the duration of the method call.

example :

```swift
struct Player {
    var name: String
    var health: Int
    var energy: Int

    static let maxHealth = 10
    mutating func restoreHealth() {
        health = Player.maxHealth
    }
    
    mutating func shareHealth(with teammate: inout Player) {
        balance(&teammate.health, &health)
    }
}
```

```swift
var oscar = Player(name: "Oscar", health: 10, energy: 10)
var maria = Player(name: "Maria", health: 5, energy: 10)
oscar.shareHealth(with: &maria)  // OK
oscar.shareHealth(with: &oscar). // Error: conflicting accesses to oscar
```

- The mutating method needs write access to self for the duration of the method, and the in-out parameter needs write access to teammate for the same duration.  

- Within the method, both self and teammate refer to the same location in memory and they overlap, producing a conflict.

---

### Conflicting Access to Properties

- structures, tuples, and enumerations are value types.
- Mutating any piece of the value mutates the whole value, meaning read or write access to one of the properties requires read or write access to the whole value. 

example :

``` swift
var playerInformation = (health: 10, energy: 20)
balance(&playerInformation.health, &playerInformation.energy)
// Error: conflicting access to properties of playerInformation
```

A write access to the tuple element requires a write access to the entire tuple. This means there are two write accesses to playerInformation with durations that overlap, causing a conflict.

-  same error appears for overlapping write accesses to the properties of a structure that’s stored in a global variable.

``` swift
var holly = Player(name: "Holly", health: 10, energy: 10)
balance(&holly.health, &holly.energy)  // Error

``` 

When the scope of struct is changed to a local variable instead of a global variable, the compiler can prove that overlapping access to stored properties of the structure is safe:

``` swift
func someFunction() {
    var oscar = Player(name: "Oscar", health: 10, energy: 10)
    balance(&oscar.health, &oscar.energy)  // OK
}
```
It's because of multiple threads could access the global variable at the same time. In the case of a local variable, that variable exists once per execution of the function. If multiple threads run someFunction() simultaneously, each thread has its own "oscar" variable, so there is no chance that thread 1's "oscar" variable access thread 2's oscar variable.