0

I read many publications on "self" in Swift and I am starting to get a gist of it, but there is still one thing that is unclear to me.

class Car {
  // 1
  let make: String
  // 2
  private(set) var color: String
  init() {
    make = "Ford"
    color = "Black"
  }
  required init(make: String, color: String) {
    self.make = make
    self.color = color
  }
// 3
  func paint(color: String) {
    self.color = color
  }
}

let car = Car(make: "Tesla", color: "Red")
car.paint("Blue")

I am trying to prove my point with help from the example above.

Several publications that I read indicate that self is used to distinguish 'color' from init() from the 'color' in the parameter from the func paint(color: String).

So when 'self color' is set in the func paint(color: String), which 'color' is it referring to? 'color' from the init() or color from the parameter of func paint(color: String)?

shim
  • 9,289
  • 12
  • 69
  • 108
jamryu
  • 668
  • 10
  • 24
  • Also what would be the reason that init() is set up to automatically inititalize the value of "make" and "color" and then require you to initialize on your own after? – jamryu May 29 '17 at 23:07
  • The call to `paint` is to indicate that the car has been painted. It now has a new color. Of course you would never call `paint` on the next line after creating the `Car` instance. – rmaddy May 29 '17 at 23:35

3 Answers3

3

self is a reference to the current instance of the class in which the code is running.

In both the init method and the paint method, it allows you to specify that you wish to set the member variable named color using the value passed in the parameter to the method that is also called color.

The paint method cannot reference the parameter passed to init at all (nor vice versa).

So, in your sample code, both methods set the color of the object to some specified value passed in to the method as a parameter.

The init method sets an initial color for the object.

The paint method then allows you to change the color of the object from that initial color.

This might be clearer if the parameters were simply named differently, e.g.:

required init(initialMake: String, initialColor: String) {
  self.make = initialMake
  self.color = initialColor
}

func paint(newColor: String) {
  self.color = newColor
}

In this case, since the functions are member methods, the self is now entirely optional since the compiler knows that color now can only mean the member called color since there is no other variable or parameter with that name, i.e. the paint method could be written simply as:

func paint(newColor: String) {
  color = newColor
}

and this would have the exact same behaviour.

However, some people prefer to keep the self prefix for clarity, even where it isn't strictly required since as well as making the intent clear it can help avoid accidental mistakes if variables or member names are changed.

Deltics
  • 22,162
  • 2
  • 42
  • 70
2

The self.color in both the init function and the paint function refer to the instance variable on the Car object.

To understand, think of the code without self:

func paint(color: String) {
    color = color
}

The function's intent is for the car's color property to be the color passed into it. But with this code, it will not reference the car's color property, and the function wont work as intended. Thus, your function needs a little help in the form of the self keyword.

Also what would be the reason that init() is set up to automatically inititalize the value of "make" and "color" and then require you to initialize on your own after?

You're passing in values with your init function, you're not "automatically initializing the value of "make" and "color". The instance variables you have created for the Car object are non-optional values (Strings), so they cannot be nil, ever. So if you initialize a Car, you have to have their values correctly set after the init function is finished.

ColdLogic
  • 7,206
  • 1
  • 28
  • 46
  • In the code you posted, the compiler knows exactly which `color` to use - the parameter. That code has no reference to the property. It's not ambiguous. But that code won't compile because `color` is implicitly declared with `let` and you can't assign a value to a `let` constant. – rmaddy May 29 '17 at 23:18
  • @rmaddy you're correct. Wasnt my intent to imply the code was something to try. Does the updated text help clarify? – ColdLogic May 29 '17 at 23:21
  • The paragraph after the code makes it sound like the compiler will be confused. It isn't. It knows exactly what each `color` reference is - the parameter. You add `self` to choose the correct `color`, not to "un-confuse" the compiler. – rmaddy May 29 '17 at 23:29
  • @rmaddy how is this? – ColdLogic May 29 '17 at 23:36
  • Seems clearer to me. – rmaddy May 29 '17 at 23:38
1

self references the object instance. Using self in Swift is optional in most cases.

It's needed in the code you posted because inside the init and the paint methods you have a parameter named color and you also want to access the property named color. Inside the methods, a reference to color will always be to the parameter. So the only way to indicate you want to reference the property named color, you must prefix the reference with self.. If you renamed the parameter so it didn't have the same name as the property, you wouldn't need self.

self.color will always mean a reference to the color property. color will first look for the nearest local variable/parameter of the same name. If found, that's what is used. If not, a property of the same name is used.

rmaddy
  • 314,917
  • 42
  • 532
  • 579