This question already has answers here:
What is "self" used for in Swift?
(10 answers)
Closed 5 years ago.
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)?
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.
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.
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.
Related
my code is:
public var color = [UIColor]()
color.append(String(UIColor(dragInViews[i]!.backgroundColor)))
this code has an error:
Argument labels '(_:)' do not match any available overloads.
I'm trying to resolve problem but I don't know. What is the problem
how to resolve my problem?
You don't need the String() part (nor the UIColor() initializer), it's already a UIColor and the array is defined as an array of UIColor, so just appending it is more than enough.
public var color = [UIColor]()
color.append(dragInViews[i]!.backgroundColor)
Note that the backgroundColor property of a UIView is already a color, so there's no point in instantiating it again.
That particular error you're seeing is because you were trying to instantiate a color with its initializer in this way UIColor(something), but the initializer that exists is UIColor(white:, alpha:) between others. Check out the documentation here.
You need to create new array of UIColor type and in which you need to append color value not string type directly color
var arrColor = [UIColor]()
arrColor.append(UIColor(dragInViews[i]?.backgroundColor ?? UIColor()))
I've just finished debugging a situation where some of my labels were not displaying any text, despite the string in question definitely containing a value (it was printing the line before). I eventually pinned it down to returning different values depending on the way I accessed the string. Please note I'm not looking for ways to work around this, I am looking for a reason I am missing as to why this behaviour happens.
I had a protocol with the following optional String property, set to default to nil:
protocol SomeProtocol {
var titleString: String? { get }
}
extension SomeProtocol {
var titleString: String? {
return nil
}
}
Which was implemented in a class with a not optional String, with title set elsewhere:
class SomeClass: SomeProtocol {
var title: String
var titleString: String {
return title
}
}
When attempting to access the value of titleString from a SomeClass object, it always returned nil, regardless of the title property. This was seen through assigning to a label like:
label.text = someClassInstance.titleString
However, when I printed the value or set the label through
label.text = "\(someClassInstance.titleString)"
everything worked, and it displayed the text.
I've narrowed the source of this down to where I've overridden the optional property with one not optional. Clearly when I access the property directly it is returned by the protocol implementation, while using it by string interpolation returns the class one. What is actually behind this behaviour?
Edit: to demonstrate this behaviour run this gist in an Xcode playground:
https://gist.github.com/CaileanWilkinson/357c17f36d04b522b9bcf1241a825d9f
I feel like this is somewhat similar to the solution of this question of mine. In that question, there is also two almost identical properties - one optional, the other non-optional. And I experienced a similar situation where Swift can't figure out which property I want.
Your titleString in SomeClass is not overriding the titleString property in the protocol. This is reflected in Xcode's suggestions:
You can access both properties like this:
someObject.titleString as String // accesses the one in SomeClass
someObject.titleString as String? // accesses the one in the protocol
My point here is that the type of the expression matters. If the type of expression swift expects is String, then it resolves to the one in SomeClass. If the expected type of the expression is String?, then it evaluates to the one in the protocol.
This explains why setting the label's text without string interpolation will call the property in the protocol (label.text is String?, so it expects a String?) and why using string interpolation will call the property in SomeClass (String interpolation expects a non-optional).
I am trying to get my head around initializers in Swift. I kind of get what they do, but I dont get why I need them.
For example: (from apples documentation)
class NamedShape {
var numberOfSides = 0
var name: String
var sideLength: Double
init(name: String, sideLength: Double) {
self.name = name
self.sideLenght = sideLength
}
}
If init should set an initial value, shouldn't self.sideLength equals an actual value, instead of just "sideLength". Or do I need to create an init to take in arguments to a function?
I'm so confused but would really appreciate if someone could help me.
The main purposes of initialisers is to initialise non-Optional variables. Your class has three variables - numberOfSides, name and sideLength. You give numberOfSides an initial value of zero, so it is happy. However, name and sideLength do not have values, which is "illegal" for a non-Optional. Therefore they must be given values in an initialiser. For example, if you comment out one of the lines in the init, you will get a compilation error because that variable will now not be initialised. And you do this when you cannot logically determine initial values at compile time - you could argue this is the case with numberOfSides too, as there is no shape that has no sides.
Elsewhere in your code you might want to validate numberOfSides is at least 1. This could be achieved by having numberOfSides undefined at compile time, and passed in the init, whereby if it's not at least 1, your initialiser could return nil (which would mean using an failable initialiser - init?).
Note that in your case, the name passed in the initialiser, is not the same as the name variable in the class. This is why you have to do:
self.name = name
To copy the name value passed in the initialiser into the variable in the class.
Quote from the Swift 3.0 office document of the Chapter: Initialization
For class instances, a constant property can be modified during initialization only by the class that introduces it. It cannot be modified by a subclass.
To my understanding the modified involves the action after the definition, aka the action after declaring and assigning value, aka re-assigning values, therefore I tried the following code.
class SurveryQuestion {
let text: String
var response: String?
init(text: String) {
self.text = "do you like music?"
self.text = text //Got an error here
}
func ask(){
print(text)
}
}
And I got an error at line self.text = text. The compiler asked me to change the property textfrom constant to variable. Isn't it says that the constant property can be modified by the initializer of the class which originally introduced it?
Question: Am I understand the word modified wrongly? Is it means the action after the declaring rather than the definition which would lead to the modified is meant to by passing a value to the constant.
I think that the documentation is not clear enough. You can set a constant property only once during initializing. You also would not be able to set it during initialization if the property's value was defined inline. Here is example.
class SomeClass {
let someProperty: String = "A"
init() {
self.someProperty = "" //ERROR: Immutable value "self.someProperty" may only be initialized once.
}
}
The compile time error //ERROR: Immutable value "self.someProperty" may only be initialized once. actually explains it well.
Currently, I am seeing something strange behavior.
class DataManager1
{
let THE_ID = "SOME_ID_STRING"
let _con1 = CKContainer(identifier: THE_ID) // error
// error: 'DataManager1.Type' does not have a member named 'THE_ID'
}
class DataManager2
{
let THE_ID = "SOME_ID_STRING"
let _con1:CKContainer?
init()
{
_con1 = CKContainer(identifier: THE_ID) // no error.
}
}
In C++ we have a defined initialization order between instance member variables. I expected something similar, but actually I couldn't find a mention for that form the manual.
Does Swift has a defined initialization order of properties? If it does, what is the rule, and where can I find the rule?
This is due to the fact that you're using a Closure (a Function is just a special case of Closure that is unnamed) to initialize the _con1 property with a default value.
From the Apple provided iBook:
If you use a closure to initialize a property, remember that the rest
of the instance has not yet been initialized at the point that the
closure is executed. This means that you cannot access any other
property values from within your closure, even if those properties
have default values. You also cannot use the implicit self property,
or call any of the instance’s methods.
Even though the note above refers specifically to closures, it seems that trying to set the default value for a property to be that of another property directly also does not work.