Swift: How to Assign a Variable by Reference, Not by Value

Can I create a reference to a variable in Swift?

There are no references for variables, but you can simulate the behavior with a Computed Variable.

Computed variables calculate their values instead of storing them, and they are written in the same way as computed properties. By writing the get and set to read and write the original variable, you can simulate the behavior you want.

override func viewDidAppear(_ animated: Bool) {

var reference: Bool {
get { return test.shared.changeMe }
set { test.shared.changeMe = newValue }
}

reference = true

print("Reference: " + String(reference))
test.shared.printValue()

}

Note: If you need access to reference outside of viewDidAppear, then declare it as a computed property on the class instead (just move it outside of the function).

Change value of variable by reference

You can accomplish this using a ReferenceWritableKeyPath.

What is a ReferenceWritableKeyPath?

Think of it as a precise description of a property in a class or struct. A real world example would be The Empire State Building instead of the address which would be 20 W 34th St, New York, NY 10001. You could tell either one to a cab driver and she could take you there.

A ReferenceWritableKeyPath is a generic type. You need to specify the class or struct name and the type of the variable you will be accessing. So your choice variable would be a ReferenceWritableKeyPath<YourClass,ColoresDeSalidas>.

Here is a standalone example:

enum ColoresDeSalidas : String {
case Negra
case Blanca
case Roja
case Azul
case Verde
}

class Foo {
var backTees = false
var option = 1

var colorHcpBackTees1: ColoresDeSalidas = .Negra
var colorHcpBackTees2: ColoresDeSalidas = .Blanca
var colorHcpFrontTees1: ColoresDeSalidas = .Roja
var colorHcpFrontTees2: ColoresDeSalidas = .Blanca

var choice: ReferenceWritableKeyPath<Foo,ColoresDeSalidas> {
if backTees && option == 1 { return \.colorHcpBackTees1 }
if backTees && option == 2 { return \.colorHcpBackTees2 }
if !backTees && option == 1 { return \.colorHcpFrontTees1 }
if !backTees && option == 2 { return \.colorHcpFrontTees2 }

fatalError("We were supposed to return a keyPath for choice")
}

func test() {
backTees = true
option = 2

print("Before: \(self.colorHcpBackTees2)")

// Now update the correct property using the choice KeyPath
self[keyPath: choice] = .Azul

print("After: \(self.colorHcpBackTees2)")

backTees = false
option = 1

// Assign it to another variable, just to show you can
let choiceFront1 = choice

option = 2

// choiceFront1 still refers to !backTees and option 1
// even though option and choice have changed
print("colorHcpFrontTees1 = \(self[keyPath: choiceFront1])")
colorHcpFrontTees1 = .Verde
print("colorHcpFrontTees1 = \(self[keyPath: choiceFront1])")

}
}

Run the test:

Foo().test()

Output:

Before: Blanca
After: Azul
colorHcpFrontTees1 = Roja
colorHcpFrontTees1 = Verde

Stopping reference variables changing the value of the original variable

The whole point of reference semantics (as used by classes) is that all variables point to the same (i.e., they reference the same) object in memory. If you don't want that behaviour, you should use value types (Struct, Enum, Array...) or create copies of your object.

If CustomClass implements the NSCopying protocol you can do:

var referenceVariable = originalVariable.copy()

If it doesn't, you'll have to find some other way to copy it or implement the protocol yourself.

Wrapping the class in a struct will just make two different structs each containing a different reference to the same object.

Assigning a reference from a class member to variable in Swift

why userDataStore doesn't point to profileInteractor.userDataStore !?

In this line:

var profileInteractor = ProfileInteractor(userDataStore: UserDataStore())

You created a new ProfileInteractor object and a new UserDataStore object. Then, you made profileInteractor refer to the ProfileInteractor object.

In the next line:

var userDataStore = profileInteractor.userDataStore

You are making userDataStore refer to the same object as profileInteractor.userDataStore. Now anything you do to the UserDataStore object by using userDataStore will be reflected on profileInteractor.userDataStore because they are essentially the same thing.

However, this line changes everything:

profileInteractor.userDataStore = nil

You made profileInteractor.userDataStore refer to nothing. This DOES NOT mean that the UserDataStore object is destroyed. This just means that you no longer can use profileInteractor.userDataStore to access the UserDataStore object. But guess what is still referring to the UserDataStore object? userDataStore!

So the answer to your question is that userDataStore did point to the same object as profileInteractor.userDataStore, until you set profileInteractor.userDataStore to nil.

What must I do, that userDataStore points to it?

Well, you already did. You just made profileInteractor.userDataStore point to something else in the next line of code.

If you want a local variable to always point to the same thing that another local variable points to, your code can get quite messy, like in Ankit Thakur's answer, with unsafe pointers and stuff. If you are passing the variable to a method, you can use the inout keyword.

Is Swift Pass By Value or Pass By Reference

Types of Things in Swift

The rule is:

  • Class instances are reference types (i.e. your reference to a class instance is effectively a pointer)

  • Functions are reference types

  • Everything else is a value type; "everything else" simply means instances of structs and instances of enums, because that's all there is in Swift. Arrays and strings are struct instances, for example. You can pass a reference to one of those things (as a function argument) by using inout and taking the address, as newacct has pointed out. But the type is itself a value type.

What Reference Types Mean For You

A reference type object is special in practice because:

  • Mere assignment or passing to function can yield multiple references to the same object

  • The object itself is mutable even if the reference to it is a constant (let, either explicit or implied).

  • A mutation to the object affects that object as seen by all references to it.

Those can be dangers, so keep an eye out. On the other hand, passing a reference type is clearly efficient because only a pointer is copied and passed, which is trivial.

What Value Types Mean For You

Clearly, passing a value type is "safer", and let means what it says: you can't mutate a struct instance or enum instance through a let reference. On the other hand, that safety is achieved by making a separate copy of the value, isn't it? Doesn't that make passing a value type potentially expensive?

Well, yes and no. It isn't as bad as you might think. As Nate Cook has said, passing a value type does not necessarily imply copying, because let (explicit or implied) guarantees immutability so there's no need to copy anything. And even passing into a var reference doesn't mean that things will be copied, only that they can be if necessary (because there's a mutation). The docs specifically advise you not to get your knickers in a twist.

store reference in variable SWIFT 4

Don't do that.

Create a data model using a class. A class is reference type

class Model {

var name : String
var email : String

init(name: String, email: String) {
self.name = name
self.email = email
}
}

Declare the data source array

var models = [Model]()

In the cell declare a property of the model (by the way class names start with a capital letter)

class CustomCell: TableViewCell {

@IBOutlet weak var textField: UITextField!

var model: Model! {
didSet {
textField.text = model.email
}
}

override func awakeFromNib() {
super.awakeFromNib()
textField.delegate = self
}
}


extension CustomCell: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
model.email = textField.text ?? ""
}
}

In cellForRow pass the model to the cell

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! CustomCell
cell.model = models[indexPath.row]
return cell
}

Due to the reference semantics the value in the model is preserved.



Related Topics



Leave a reply



Submit