The Ins and Outs of InOut Variables

When helping iOS developers prepare for technical interviews, I usually focus on problem-solving techniques rather than specific syntax or the Apple SDK. However, there are some key coding features iOS developers should be aware of. Most of us are familiar with examples of Optionals, Closures and Generics. However, the unique InOut variable can also help developers extend the depth and complexity of their solutions. Let’s review some scenarios of when this can be applied.

Value Types

When it comes to Swift syntax, a nice feature is that objects are either treated as a reference or value type. When working with value types, a new memory is allocated for each instance or assignment. For example:

let apple = “Apple”
let orange = “Orange”

//not permitted - cannot reassign a constant value
orange = apple

How this works should be familiar. Working with value types such as let constants or struct types is the preferred way to write programs in Swift, as all objects have their defined space and are free of pointer references or other scope complexities. As part of Swift’s evolution, this safety has also been applied at the function level. As a result, functions (by default) pass variables by copy. This implies function parameters are declared as constants. For example:

 

Like this article? Get more content like this in the iOS Computer Science Lab.

 
func reverse1(using element: String) -> String {    
    //not permitted - cannot mutate a constant  
    element = element.reversed()
    return String(element)
}

func reverse2(using element: String) -> String {
    //a new memory allocated. works as expected
    let results = element.reversed()    
    return String(results)
}

Reference Types

Even though programming by value is a great strategy, there are still benefits to programming by reference. This can be seen with recursive data structures:

class Node <T>{
  var key: T?
  var next: Node<T>?  //value by reference
}

The ability to refer to a new instance by reference allows us to “chain” seemingly independent objects together - an important strategy when building complex structures like a Tree, Queue or Graph. The ability to pass function parameters by reference is also advantageous. Known in Swift as an Inout variable, consider the following:

class StackSet <T: Hashable>{

    var set = Set<T>()
    var stack = Stack<T>() //a custom type..
    
    //add item
    func insert(_ element: T) {
        
        var value = element

        //add element to set
        set.insert(value)
        
        //push a reference - preserve order
        push(&value)
    }
        
    //preserve order of elements
    private func push(_ reference: inout T) {
        stack.push(element: reference)
    }       
}

We’ve been asked to build a simple solution to track the order of items added to a Set collection. In Swift, Sets differ from the standard Array and Dictionary objects because their order of items isn’t guaranteed. To compensate, we’ve added a secondary data structure known as a Stack to act as the ledger.

Similar to a call stack used in software development, its main purpose is to track the order of elements and/or operations. To avoid obvious issues with data duplication, the push() function sends a reference of each value to the Stack. Identified with the “&” character, we can interpret this as the “memory address of”. Even though the inout function accepts a reference (not a value) it still has a familiar coding style. In this case, push() accepts a Swift generic type that also conforms to the popular Hashable protocol.

Another interesting feature is that push() does not require a return value. As a function that stores references, any change in the original values will be automatically reflected in any subsequent reference. In some cases, this type of feature can wreak havoc in a program. However, this is the exact functionality we are seeking, allowing our Set type to maintain a single source of truth.

The Supporting Stack

The final piece involves the composition of our custom structure. One of the more applicable types used in everyday development, a Stack can be represented using key methods from a native Swift Array:

struct Stack <T> {
  var elements: [T] = [T]()
  
mutating func push(element: T) 
      
  

  mutating func pop() -> T? {
      return elements.popLast()
  }

  func peek() -> T? {
      return elements.last
  }
}