Static vs class functions/variables in Swift classes?

class initailaizer를 이해하는데 좋은 예시 (swift docs 에서 가져온 내용)

original source https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-ID203

Designated and Convenience Initializers in Action

The following example shows designated initializers, convenience initializers, and automatic initializer inheritance in action. This example defines a hierarchy of three classes called Food, RecipeIngredient, and ShoppingListItem, and demonstrates how their initializers interact.

The base class in the hierarchy is called Food, which is a simple class to encapsulate the name of a foodstuff. The Food class introduces a single String property called name and provides two initializers for creating Food instances:

class Food {
   var name: String
   init(name: String) {
       self.name = name
   }
   convenience init() {
       self.init(name: “[Unnamed]”)
   }
}

The figure below shows the initializer chain for the Food class:

Classes do not have a default memberwise initializer, and so the Food class provides a designated initializer that takes a single argument called name. This initializer can be used to create a new Food instance with a specific name:

let namedMeat = Food(name: “Bacon”)
// namedMeat’s name is “Bacon”

The init(name: String) initializer from the Food class is provided as a designated initializer, because it ensures that all stored properties of a new Food instance are fully initialized. The Food class does not have a superclass, and so the init(name: String) initializer does not need to call super.init() to complete its initialization.

The Food class also provides a convenience initializer, init(), with no arguments. The init() initializer provides a default placeholder name for a new food by delegating across to the Food class’s init(name: String) with a name value of [Unnamed]:

let mysteryMeat = Food()
// mysteryMeat’s name is “[Unnamed]”

The second class in the hierarchy is a subclass of Food called RecipeIngredient. The RecipeIngredient class models an ingredient in a cooking recipe. It introduces an Int property called quantity (in addition to the name property it inherits from Food) and defines two initializers for creating RecipeIngredient instances:

class RecipeIngredient: Food {
   var quantity: Int
   init(name: String, quantity: Int) {
       self.quantity = quantity
       super.init(name: name)
   }
   override convenience init(name: String) {
       self.init(name: name, quantity: 1)
   }
}

The figure below shows the initializer chain for the RecipeIngredient class:

The RecipeIngredient class has a single designated initializer, init(name: String, quantity: Int), which can be used to populate all of the properties of a new RecipeIngredient instance. This initializer starts by assigning the passed quantity argument to the quantity property, which is the only new property introduced by RecipeIngredient. After doing so, the initializer delegates up to the init(name: String) initializer of the Food class. This process satisfies safety check 1 from Two-Phase Initialization above.

RecipeIngredient also defines a convenience initializer, init(name: String), which is used to create a RecipeIngredient instance by name alone. This convenience initializer assumes a quantity of 1 for any RecipeIngredient instance that is created without an explicit quantity. The definition of this convenience initializer makes RecipeIngredient instances quicker and more convenient to create, and avoids code duplication when creating several single-quantity RecipeIngredient instances. This convenience initializer simply delegates across to the class’s designated initializer, passing in a quantity value of 1.

The init(name: String) convenience initializer provided by RecipeIngredient takes the same parameters as the init(name: String) designated initializer from Food. Because this convenience initializer overrides a designated initializer from its superclass, it must be marked with the override modifier (as described in Initializer Inheritance and Overriding).

Even though RecipeIngredient provides the init(name: String) initializer as a convenience initializer, RecipeIngredient has nonetheless provided an implementation of all of its superclass’s designated initializers. Therefore, RecipeIngredient automatically inherits all of its superclass’s convenience initializers too.

In this example, the superclass for RecipeIngredient is Food, which has a single convenience initializer called init(). This initializer is therefore inherited by RecipeIngredient. The inherited version of init() functions in exactly the same way as the Food version, except that it delegates to the RecipeIngredient version of init(name: String) rather than the Food version.

All three of these initializers can be used to create new RecipeIngredient instances:

let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: “Bacon”)
let sixEggs = RecipeIngredient(name: “Eggs”, quantity: 6)

The third and final class in the hierarchy is a subclass of RecipeIngredient called ShoppingListItem. The ShoppingListItem class models a recipe ingredient as it appears in a shopping list.

Every item in the shopping list starts out as “unpurchased”. To represent this fact, ShoppingListItemintroduces a Boolean property called purchased, with a default value of false. ShoppingListItem also adds a computed description property, which provides a textual description of a ShoppingListItem instance:

class ShoppingListItem: RecipeIngredient {
   var purchased = false
   var description: String {
       var output = “(quantity) x (name)”
       output += purchased ? “ ✔” : “ ✘”
       return output
   }
}

NOTE

ShoppingListItem does not define an initializer to provide an initial value for purchased, because items in a shopping list (as modeled here) always start out unpurchased.

Because it provides a default value for all of the properties it introduces and does not define any initializers itself, ShoppingListItem automatically inherits all of the designated and convenience initializers from its superclass.

The figure below shows the overall initializer chain for all three classes:

You can use all three of the inherited initializers to create a new ShoppingListItem instance:

var breakfastList = [
   ShoppingListItem(),
   ShoppingListItem(name: “Bacon”),
   ShoppingListItem(name: “Eggs”, quantity: 6),
]
breakfastList[0].name = “Orange juice”
breakfastList[0].purchased = true
for item in breakfastList {
   print(item.description)
}
// 1 x Orange juice ✔
// 1 x Bacon ✘
// 6 x Eggs ✘

Here, a new array called breakfastList is created from an array literal containing three new ShoppingListItem instances. The type of the array is inferred to be [ShoppingListItem]. After the array is created, the name of the ShoppingListItem at the start of the array is changed from "[Unnamed]" to "Orange juice" and it is marked as having been purchased. Printing the description of each item in the array shows that their default states have been set as expected.

Swift Tutorial: Initialization In Depth, Part 2/2

Designated Initializers and Convenience Initializers in Swift

Swift type properties – Cosmin Mircea – Medium

All about Properties in swift – Abhimuralidharan – Medium

Methods

Methods are functions that are associated with a particular type. Classes, structures, and enumerations can all define instance methods, which encapsulate specific tasks and functionality for working with an instance of a given type. Classes, structures, and enumerations can also define type methods, which are associated with the type itself.

Instance Methods

Instance methods are functions that belong to instances of a particular class, structure, or enumeration. They support the functionality of those instances, either by providing ways to access and modify instance properties, or by providing functionality related to the instance’s purpose. Instance methods have exactly the same syntax as functions, as described in Functions.

You write an instance method within the opening and closing braces of the type it belongs to. An instance method has implicit access to all other instance methods and properties of that type. An instance method can be called only on a specific instance of the type it belongs to. It cannot be called in isolation without an existing instance.

Here’s an example that defines a simple Counter class, which can be used to count the number of times an action occurs:

class Counter {
   var count = 0
   func increment() {
       count += 1
   }

//  “amount” is parameter name
   func increment(by amount: Int) {
       count += amount
   }
   func reset() {
       count = 0
   }
}

The Counter class defines three instance methods:

let counter = Counter()
// the initial counter value is 0
counter.increment()
// the counter’s value is now 1

// by is argument label
counter.increment(by: 5)
// the counter’s value is now 6
counter.reset()
// the counter’s value is now 0

The self Property

Every instance of a type has an implicit property called self, which is exactly equivalent to the instance itself. You use the self property to refer to the current instance within its own instance methods.

The increment() method in the example above could have been written like this:

func increment() {
   self.count += 1
}

In practice, you don’t need to write self in your code very often. If you don’t explicitly write self, Swift assumes that you are referring to a property or method of the current instance whenever you use a known property or method name within a method. This assumption is demonstrated by the use of count (rather than self.count) inside the three instance methods for Counter.

The main exception to this rule occurs when a parameter name for an instance method has the same name as a property of that instance. In this situation, the parameter name takes precedence, and it becomes necessary to refer to the property in a more qualified way. You use the self property to distinguish between the parameter name and the property name.

Here, self disambiguates between a method parameter called x and an instance property that is also called x:

struct Point {
   var x = 0.0, y = 0.0
   func isToTheRightOf(x: Double) -> Bool {
       return self.x > x
   }
}
let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
   print(“This point is to the right of the line where x == 1.0”)
}
// Prints “This point is to the right of the line where x == 1.0”

(참고 https://medium.com/@andrea.prearo/reference-and-value-types-in-swift-dad40ea76226)

(참고 tumblr #swift  #var  #let  #reference  #value  #type  #mutable  #immutable  #immutablity  #variable )

Modifying Value Types from Within Instance Methods

Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.

However, if you need to modify the properties of your structure or enumeration within a particular method, you can opt in to mutating behavior for that method. The method can then mutate (that is, change) its properties from within the method, and any changes that it makes are written back to the original structure when the method ends. The method can also assign a completely new instance to its implicit self property, and this new instance will replace the existing one when the method ends.

You can opt in to this behavior by placing the mutating keyword before the func keyword for that method:

struct Point {
   var x = 0.0, y = 0.0
   mutating func moveBy(x deltaX: Double, y deltaY: Double) {
       x += deltaX
       y += deltaY
   }
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print(“The point is now at ((somePoint.x), (somePoint.y))”)
// Prints “The point is now at (3.0, 4.0)”

Note that you cannot call a mutating method on a constant of structure type, because its properties cannot be changed, even if they are variable properties, as described in Stored Properties of Constant Structure Instances:

let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0)
// this will report an error

Assigning to self Within a Mutating Method

Mutating methods can assign an entirely new instance to the implicit self property. The Point example shown above could have been written in the following way instead:

struct Point {
   var x = 0.0, y = 0.0
   mutating func moveBy(x deltaX: Double, y deltaY: Double) {
       self = Point(x: x + deltaX, y: y + deltaY)
   }
}

Mutating methods for enumerations can set the implicit self parameter to be a different case from the same enumeration:

enum TriStateSwitch {
   case off, low, high
   mutating func next() {
       switch self {
       case .off:
           self = .low
       case .low:
           self = .high
       case .high:
           self = .off
       }
   }
}
var ovenLight = TriStateSwitch.low
ovenLight.next()
// ovenLight is now equal to .high
ovenLight.next()
// ovenLight is now equal to .off