(참고사항 https://medium.com/@abhimuralidharan/all-about-protocols-in-swift-11a72d6ea354

tumblr #swift #protocol )

Protocols

A protocol defines a blueprint of methods, properties, and other requirements. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol.

In addition to specifying requirements that conforming types must implement, you can extend a protocol to implement some of these requirements or to implement additional functionality that conforming types can take advantage of.

Protocol Syntax

define protocols

protocol SomeProtocol {
   // protocol definition goes here
}

struct SomeStructure: FirstProtocol, AnotherProtocol {
   // structure definition goes here
}

If a class has a superclass, list the superclass name before any protocols it adopts, followed by a comma:

class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
   // class definition goes here
}

Property Requirements

A protocol can require any conforming type to provide an instance property or type property with a particular name and type. The protocol doesn’t specify whether the property should be a stored property or a computed property—it only specifies the required property name and type. The protocol also specifies whether each property must be gettable or gettable and settable.

If a protocol requires a property to be gettable and settable, that property requirement can’t be fulfilled by a constant stored property or a read-only computed property. If the protocol only requires a property to be gettable, the requirement can be satisfied by any kind of property, and it’s valid for the property to be also settable if this is useful for your own code.

Property requirements are always declared as variable properties, prefixed with the var keyword. Gettable and settable properties are indicated by writing { get set } after their type declaration, and gettable properties are indicated by writing { get }.

protocol SomeProtocol {
   var mustBeSettable: Int { get set }
   var doesNotNeedToBeSettable: Int { get }
}

Always prefix type property requirements with the static keyword when you define them in a protocol. This rule pertains even though type property requirements can be prefixed with the class or static keyword when implemented by a class(하부 implementing class , structure, enumeration에서 수행될때는 class, static 사용가능하다는 이야기):

protocol FullyNamed {
   var fullName: String { get }
}

struct Person: FullyNamed {
   var fullName: String
}
let john = Person(fullName: “John Appleseed”)
// john.fullName is “John Appleseed”

class Starship: FullyNamed {
   var prefix: String?
   var name: String
   init(name: String, prefix: String? = nil) {
       self.name = name
       self.prefix = prefix
   }
   var fullName: String {
       return (prefix != nil ? prefix! + “ ” : “”) + name
   }
}
var ncc1701 = Starship(name: “Enterprise”, prefix: “USS”)
// ncc1701.fullName is “USS Enterprise”

Method Requirements

These methods are written as part of the protocol’s definition in exactly the same way as for normal instance and type methods, but without curly braces or a method body. Variadic parameters are allowed, subject to the same rules as for normal methods. Default values, however, can’t be specified for method parameters within a protocol’s definition.

As with type property requirements, you always prefix type method requirements with the static keyword when they’re defined in a protocol. This is true even though type method requirements are prefixed with the class or static keyword when implemented by a class:

protocol SomeProtocol {

   // type method
   static func someTypeMethod()
}

protocol RandomNumberGenerator {
   func random() -> Double
}

class LinearCongruentialGenerator: RandomNumberGenerator {
   var lastRandom = 42.0
   let m = 139968.0
   let a = 3877.0
   let c = 29573.0
   func random() -> Double {
       lastRandom = ((lastRandom * a + c).truncatingRemainder(dividingBy:m))
       return lastRandom / m
   }
}
let generator = LinearCongruentialGenerator()
print(“Here’s a random number: (generator.random())”)
// Prints “Here’s a random number: 0.37464991998171”
print(“And another one: (generator.random())”)
// Prints “And another one: 0.729023776863283”

Mutating Method Requirements

It’s sometimes necessary for a method to modify (or mutate) the instance it belongs to. For instance methods on value types (that is, structures and enumerations) you place the mutating keyword before a method’s func keyword to indicate that the method is allowed to modify the instance it belongs to and any properties of that instance. This process is described in Modifying Value Types from Within Instance Methods.

If you define a protocol instance method requirement that is intended to mutate instances of any type that adopts the protocol, mark the method with the mutating keyword as part of the protocol’s definition. This enables structures and enumerations to adopt the protocol and satisfy that method requirement.

NOTE

If you mark a protocol instance method requirement as mutating, you don’t need to write the mutatingkeyword when writing an implementation of that method for a class. The mutating keyword is only used by structures and enumerations.(class에서는 본래 mutating을 사용하지 않는다.)

protocol Togglable {
   mutating func toggle()
}

enum OnOffSwitch: Togglable {
   case off, on
   mutating func toggle() {
       switch self {
       case .off:
           self = .on
       case .on:
           self = .off
       }
   }
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
// lightSwitch is now equal to .on

Initializer Requirements

Protocols can require specific initializers to be implemented by conforming types. You write these initializers as part of the protocol’s definition in exactly the same way as for normal initializers, but without curly braces or an initializer body:

protocol SomeProtocol {
   init(someParameter: Int)
}

Class Implementations of Protocol Initializer Requirements

You can implement a protocol initializer requirement on a conforming class as either a designated initializer or a convenience initializer. In both cases, you must mark the initializer implementation with the required modifier:

class SomeClass: SomeProtocol {
   required init(someParameter: Int) {
       // initializer implementation goes here
   }
}

The use of the required modifier ensures that you provide an explicit or inherited implementation of the initializer requirement on all subclasses of the conforming class, such that they also conform to the protocol.

For more information on required initializers, see Required Initializers.

NOTE

You don’t need to mark protocol initializer implementations with the required modifier on classes that are marked with the final modifier, because final classes can’t subclassed. For more on the final modifier, see Preventing Overrides.

If a subclass overrides a designated initializer from a superclass, and also implements a matching initializer requirement from a protocol, mark the initializer implementation with both the required and overridemodifiers:

protocol SomeProtocol {
   init()
}
class SomeSuperClass {
   init() {
       // initializer implementation goes here
   }
}
class SomeSubClass: SomeSuperClass, SomeProtocol {
   // “required” from SomeProtocol conformance; “override” from SomeSuperClass
   required override init() {
       // initializer implementation goes here
   }
}

Failable Initializer Requirements

Protocols can define failable initializer requirements for conforming types, as defined in Failable Initializers.

A failable initializer requirement can be satisfied by a failable or nonfailable initializer on a conforming type. A nonfailable initializer requirement can be satisfied by a nonfailable initializer or an implicitly unwrapped failable initializer.

Protocols as Types

protocol is a type, you can use a protocol in many places where other types are allowed, including:

  • As a parameter type or return type in a function, method, or initializer
  • As the type of a constant, variable, or property
  • As the type of items in an array, dictionary, or other container

NOTE

Because protocols are types, begin their names with a capital letter 

class Dice {
   let sides: Int
   let generator: RandomNumberGenerator
   init(sides: Int, generator: RandomNumberGenerator) {
       self.sides = sides
       self.generator = generator
   }
   func roll() -> Int {
       return Int(generator.random() * Double(sides)) + 1
   }
}

This example defines a new class called Dice, which represents an n-sided dice for use in a board game. Dice instances have an integer property called sides, which represents how many sides they have, and a property called generator, which provides a random number generator from which to create dice roll values.

The generator property is of type RandomNumberGenerator. Therefore, you can set it to an instance of any type that adopts the RandomNumberGenerator protocol. Nothing else is required of the instance you assign to this property, except that the instance must adopt the RandomNumberGenerator protocol.

Here’s how the Dice class can be used to create a six-sided dice with a LinearCongruentialGeneratorinstance as its random number generator:

var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
for _ in 1…5 {
   print(“Random dice roll is (d6.roll())”)
}
// Random dice roll is 3
// Random dice roll is 5
// Random dice roll is 4
// Random dice roll is 5
// Random dice roll is 4

Delegation

Delegation is a design pattern that enables a class or structure to hand off (or delegate) some of its responsibilities to an instance of another type. This design pattern is implemented by defining a protocol that encapsulates the delegated responsibilities, such that a conforming type (known as a delegate) is guaranteed to provide the functionality that has been delegated. Delegation can be used to respond to a particular action, or to retrieve data from an external source without needing to know the underlying type of that source.

The example below defines two protocols for use with dice-based board games:

protocol DiceGame {
   var dice: Dice { get }
   func play()
}
protocol DiceGameDelegate {
   func gameDidStart(_ game: DiceGame)
   func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
   func gameDidEnd(_ game: DiceGame)
}

class SnakesAndLadders: DiceGame {
   let finalSquare = 25
   let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
   var square = 0
   var board: [Int]
   init() {
       board = Array(repeating: 0, count: finalSquare + 1)
       board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
       board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
   }
   var delegate: DiceGameDelegate?
   func play() {
       square = 0
       delegate?.gameDidStart(self)
       gameLoop: while square != finalSquare {
           let diceRoll = dice.roll()
           delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
           switch square + diceRoll {
           case finalSquare:
               break gameLoop
           case let newSquare where newSquare > finalSquare:
               continue gameLoop
           default:
               square += diceRoll
               square += board[square]
           }
       }
       delegate?.gameDidEnd(self)
   }
}

class DiceGameTracker: DiceGameDelegate {
   var numberOfTurns = 0
   func gameDidStart(_ game: DiceGame) {
       numberOfTurns = 0
       if game is SnakesAndLadders {
           print(“Started a new game of Snakes and Ladders”)
       }
       print(“The game is using a (game.dice.sides)-sided dice”)
   }
   func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
       numberOfTurns += 1
       print(“Rolled a (diceRoll)”)
   }
   func gameDidEnd(_ game: DiceGame) {
       print(“The game lasted for (numberOfTurns) turns”)
   }
}

let tracker = DiceGameTracker()
let game = SnakesAndLadders()
game.delegate = tracker
game.play()
// Started a new game of Snakes and Ladders
// The game is using a 6-sided dice
// Rolled a 3
// Rolled a 5
// Rolled a 4
// Rolled a 5
// The game lasted for 4 turns

Adding Protocol Conformance with an Extension

You can extend an existing type to adopt and conform to a new protocol, even if you don’t have access to the source code for the existing type. Extensions can add new properties, methods, and subscripts to an existing type, and are therefore able to add any requirements that a protocol may demand. For more about extensions, see Extensions.

NOTE

Existing instances of a type automatically adopt and conform to a protocol when that conformance is added to the instance’s type in an extension.

protocol TextRepresentable {
   var textualDescription: String { get }
}

extension Dice: TextRepresentable {
   var textualDescription: String {
       return “A (sides)-sided dice”
   }
}

let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
print(d12.textualDescription)
// Prints “A 12-sided dice”

extension SnakesAndLadders: TextRepresentable {
   var textualDescription: String {
       return “A game of Snakes and Ladders with (finalSquare) squares”
   }
}
print(game.textualDescription)
// Prints “A game of Snakes and Ladders with 25 squares”

Declaring Protocol Adoption with an Extension

If a type already conforms to all of the requirements of a protocol, but has not yet stated that it adopts that protocol, you can make it adopt the protocol with an empty extension:

struct Hamster {
   var name: String
   var textualDescription: String {
       return “A hamster named (name)”
   }
}

extension Hamster: TextRepresentable {}

Instances of Hamster can now be used wherever TextRepresentable is the required type:

let simonTheHamster = Hamster(name: “Simon”)
let somethingTextRepresentable: TextRepresentable = simonTheHamster
print(somethingTextRepresentable.textualDescription)
// Prints “A hamster named Simon”

NOTE

Types don’t automatically adopt a protocol just by satisfying its requirements. They must always explicitly declare their adoption of the protocol.

Collections of Protocol Types

A protocol can be used as the type to be stored in a collection such as an array or a dictionary, as mentioned in Protocols as Types. This example creates an array of TextRepresentable things:

let things: [TextRepresentable] = [game, d12, simonTheHamster]

for thing in things {
   print(thing.textualDescription)
}
// A game of Snakes and Ladders with 25 squares
// A 12-sided dice
// A hamster named Simon

Note that the thing constant is of type TextRepresentable. It’s not of type Dice, or DiceGame, or Hamster.

Protocol Inheritance

A protocol can inherit one or more other protocols and can add further requirements on top of the requirements it inherits. The syntax for protocol inheritance is similar to the syntax for class inheritance, but with the option to list multiple inherited protocols, separated by commas: (class의 경우는 하나의 super class만을 inheriting할수있지만 protocol은 여러개의 protocol을 inherit 할수있다.)

protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
   // protocol definition goes here
}

protocol PrettyTextRepresentable: TextRepresentable {
   var prettyTextualDescription: String { get }
}

This example defines a new protocol, PrettyTextRepresentable, which inherits from TextRepresentable. Anything that adopts PrettyTextRepresentable must satisfy all of the requirements enforced by TextRepresentable, plus the additional requirements enforced by PrettyTextRepresentable. In this example, PrettyTextRepresentable adds a single requirement to provide a gettable property called prettyTextualDescription that returns a String.

extension SnakesAndLadders: PrettyTextRepresentable {
   var prettyTextualDescription: String {
       var output = textualDescription + “:n”
       for index in 1…finalSquare {
           switch board[index] {
           case let ladder where ladder > 0:
               output += “▲ ”
           case let snake where snake < 0:
               output += “▼ ”
           default:
               output += “○ ”
           }
       }
       return output
   }
}

print(game.prettyTextualDescription)
// A game of Snakes and Ladders with 25 squares:
// ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○

Class-Only Protocols

(structure,enumeration에서는 conform하지 못하고 class에서만 

conform할수 잇게 만드는 방법)

You can limit protocol adoption to class types (and not structures or enumerations) by adding the AnyObjectprotocol to a protocol’s inheritance list.

protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
   // class-only protocol definition goes here
}

In the example above, SomeClassOnlyProtocol can only be adopted by class types. It’s a compile-time error to write a structure or enumeration definition that tries to adopt SomeClassOnlyProtocol.

NOTE

Use a class-only protocol when the behavior defined by that protocol’s requirements assumes or requires that a conforming type has reference semantics rather than value semantics. For more on reference and value semantics, see Structures and Enumerations Are Value Types and Classes Are Reference Types.

Protocol Composition

(두개이상의  protocol을 수행하는 어떤 타입을 표현한다.  예를 들어 a & b는 a와 b protocol을 conform하는 어떤 type 을 의미)

It can be useful to require a type to conform to multiple protocols at the same time. You can combine multiple protocols into a single requirement with a protocol composition. Protocol compositions behave as if you defined a temporary local protocol that has the combined requirements of all protocols in the composition. Protocol compositions don’t define any new protocol types.

Protocol compositions have the form SomeProtocol & AnotherProtocol. You can list as many protocols as you need, separating them with ampersands (&). In addition to its list of protocols, a protocol composition can also contain one class type, which you can use to specify a required superclass.

protocol Named {
   var name: String { get }
}
protocol Aged {
   var age: Int { get }
}
struct Person: Named, Aged {
   var name: String
   var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
   print(“Happy birthday, (celebrator.name), you’re (celebrator.age)!”)
}
let birthdayPerson = Person(name: “Malcolm”, age: 21)
wishHappyBirthday(to: birthdayPerson)
// Prints “Happy birthday, Malcolm, you’re 21!”

In this example, the Named protocol has a single requirement for a gettable String property called name. The Aged protocol has a single requirement for a gettable Int property called age. Both protocols are adopted by a structure called Person.

The example also defines a wishHappyBirthday(to:) function. The type of the celebrator parameter is Named & Aged, which means “any type that conforms to both the Named and Aged protocols.” It doesn’t matter which specific type is passed to the function, as long as it conforms to both of the required protocols.

class Location {
   var latitude: Double
   var longitude: Double
   init(latitude: Double, longitude: Double) {
       self.latitude = latitude
       self.longitude = longitude
   }
}
class City: Location, Named {
   var name: String
   init(name: String, latitude: Double, longitude: Double) {
       self.name = name
       super.init(latitude: latitude, longitude: longitude)
   }
}
func beginConcert(in location: Location & Named) {
   print(“Hello, (location.name)!”)
}
let seattle = City(name: “Seattle”, latitude: 47.6, longitude: -122.3)
beginConcert(in: seattle)
// Prints “Hello, Seattle!”

Checking for Protocol Conformance

You can use the is and as operators described in Type Casting to check for protocol conformance, and to cast to a specific protocol. Checking for and casting to a protocol follows exactly the same syntax as checking for and casting to a type:

  • The is operator returns true if an instance conforms to a protocol and returns false if it doesn’t.
  • The as? version of the downcast operator returns an optional value of the protocol’s type, and this value is nil if the instance doesn’t conform to that protocol.
  • The as! version of the downcast operator forces the downcast to the protocol type and triggers a runtime error if the downcast doesn’t succeed.

protocol HasArea {
   var area: Double { get }
}

class Circle: HasArea {
   let pi = 3.1415927
   var radius: Double
   var area: Double { return pi * radius * radius }
   init(radius: Double) { self.radius = radius }
}

class Country: HasArea {
   var area: Double
   init(area: Double) { self.area = area }
}

class Animal {
   var legs: Int
   init(legs: Int) { self.legs = legs }
}

let objects: [AnyObject] = [
   Circle(radius: 2.0),
   Country(area: 243_610),
   Animal(legs: 4)
]

for object in objects {
   if let objectWithArea = object as? HasArea {
       print(“Area is (objectWithArea.area)”)
   } else {
       print(“Something that doesn’t have an area”)
   }
}
// Area is 12.5663708
// Area is 243610.0
// Something that doesn’t have an area

Protocol Extensions

(protocol을 extension해서 이 protocol을 conform하는 모든 class,structure,enumeration이 특정 functions, properties를 기본적으로 가지게 하는 방법)

Protocols can be extended to provide method and property implementations to conforming types. This allows you to define behavior on protocols themselves, rather than in each type’s individual conformance or in a global function.

extension RandomNumberGenerator {
   func randomBool() -> Bool {
       return random() > 0.5
   }
}

By creating an extension on the protocol, all conforming types automatically gain this method implementation without any additional modification.

let generator = LinearCongruentialGenerator()
print(“Here’s a random number: (generator.random())”)
// Prints “Here’s a random number: 0.37464991998171”
print(“And here’s a random Boolean: (generator.randomBool())”)
// Prints “And here’s a random Boolean: true”

Providing Default Implementations

You can use protocol extensions to provide a default implementation to any method or computed property requirement of that protocol. If a conforming type provides its own implementation of a required method or property, that implementation will be used instead of the one provided by the extension.

NOTE

Protocol requirements with default implementations provided by extensions are distinct from optional protocol requirements. Although conforming types don’t have to provide their own implementation of either, requirements with default implementations can be called without optional chaining.

Adding Constraints to Protocol Extensions

When you define a protocol extension, you can specify constraints that conforming types must satisfy before the methods and properties of the extension are available. You write these constraints after the name of the protocol you’re extending using a generic where clause, as described in Generic Where Clauses.

For instance, you can define an extension to the Collection protocol that applies to any collection whose elements conform to the TextRepresentable protocol from the example above.

extension Collection where Iterator.Element: TextRepresentable {
   var textualDescription: String {
       let itemsAsText = self.map { $0.textualDescription }
       return “[” + itemsAsText.joined(separator: “, ”) + “]”
   }
}

The textualDescription property returns the textual description of the entire collection by concatenating the textual representation of each element in the collection into a comma-separated list, enclosed in brackets.

Consider the Hamster structure from before, which conforms to the TextRepresentable protocol, and an array of Hamster values:

let murrayTheHamster = Hamster(name: “Murray”)
let morganTheHamster = Hamster(name: “Morgan”)
let mauriceTheHamster = Hamster(name: “Maurice”)
let hamsters = [murrayTheHamster, morganTheHamster, mauriceTheHamster]

Because Array conforms to Collection and the array’s elements conform to the TextRepresentable protocol, the array can use the textualDescription property to get a textual representation of its contents:

print(hamsters.textualDescription)
// Prints “[A hamster named Murray, A hamster named Morgan, A hamster named Maurice]”

NOTE

If a conforming type satisfies the requirements for multiple constrained extensions that provide implementations for the same method or property, Swift will use the implementation corresponding to the most specialized constraints.

(참고사항 https://medium.com/@abhimuralidharan/all-about-protocols-in-swift-11a72d6ea354

tumblr #swift #protocol )

Protocols

A protocol defines a blueprint of methods, properties, and other requirements. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol.

In addition to specifying requirements that conforming types must implement, you can extend a protocol to implement some of these requirements or to implement additional functionality that conforming types can take advantage of.

Protocol Syntax

define protocols

protocol SomeProtocol {
   // protocol definition goes here
}

struct SomeStructure: FirstProtocol, AnotherProtocol {
   // structure definition goes here
}

If a class has a superclass, list the superclass name before any protocols it adopts, followed by a comma:

class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
   // class definition goes here
}

Property Requirements

A protocol can require any conforming type to provide an instance property or type property with a particular name and type. The protocol doesn’t specify whether the property should be a stored property or a computed property—it only specifies the required property name and type. The protocol also specifies whether each property must be gettable or gettable and settable.

If a protocol requires a property to be gettable and settable, that property requirement can’t be fulfilled by a constant stored property or a read-only computed property. If the protocol only requires a property to be gettable, the requirement can be satisfied by any kind of property, and it’s valid for the property to be also settable if this is useful for your own code.

Property requirements are always declared as variable properties, prefixed with the var keyword. Gettable and settable properties are indicated by writing { get set } after their type declaration, and gettable properties are indicated by writing { get }.

protocol SomeProtocol {
   var mustBeSettable: Int { get set }
   var doesNotNeedToBeSettable: Int { get }
}

Always prefix type property requirements with the static keyword when you define them in a protocol. This rule pertains even though type property requirements can be prefixed with the class or static keyword when implemented by a class(하부 implementing class , structure, enumeration에서 수행될때는 class, static 사용가능하다는 이야기):

protocol FullyNamed {
   var fullName: String { get }
}

struct Person: FullyNamed {
   var fullName: String
}
let john = Person(fullName: “John Appleseed”)
// john.fullName is “John Appleseed”

class Starship: FullyNamed {
   var prefix: String?
   var name: String
   init(name: String, prefix: String? = nil) {
       self.name = name
       self.prefix = prefix
   }
   var fullName: String {
       return (prefix != nil ? prefix! + “ ” : “”) + name
   }
}
var ncc1701 = Starship(name: “Enterprise”, prefix: “USS”)
// ncc1701.fullName is “USS Enterprise”

Method Requirements

These methods are written as part of the protocol’s definition in exactly the same way as for normal instance and type methods, but without curly braces or a method body. Variadic parameters are allowed, subject to the same rules as for normal methods. Default values, however, can’t be specified for method parameters within a protocol’s definition.

As with type property requirements, you always prefix type method requirements with the static keyword when they’re defined in a protocol. This is true even though type method requirements are prefixed with the class or static keyword when implemented by a class:

protocol SomeProtocol {

   // type method
   static func someTypeMethod()
}

protocol RandomNumberGenerator {
   func random() -> Double
}

class LinearCongruentialGenerator: RandomNumberGenerator {
   var lastRandom = 42.0
   let m = 139968.0
   let a = 3877.0
   let c = 29573.0
   func random() -> Double {
       lastRandom = ((lastRandom * a + c).truncatingRemainder(dividingBy:m))
       return lastRandom / m
   }
}
let generator = LinearCongruentialGenerator()
print(“Here’s a random number: (generator.random())”)
// Prints “Here’s a random number: 0.37464991998171”
print(“And another one: (generator.random())”)
// Prints “And another one: 0.729023776863283”

Mutating Method Requirements

It’s sometimes necessary for a method to modify (or mutate) the instance it belongs to. For instance methods on value types (that is, structures and enumerations) you place the mutating keyword before a method’s func keyword to indicate that the method is allowed to modify the instance it belongs to and any properties of that instance. This process is described in Modifying Value Types from Within Instance Methods.

If you define a protocol instance method requirement that is intended to mutate instances of any type that adopts the protocol, mark the method with the mutating keyword as part of the protocol’s definition. This enables structures and enumerations to adopt the protocol and satisfy that method requirement.

NOTE

If you mark a protocol instance method requirement as mutating, you don’t need to write the mutatingkeyword when writing an implementation of that method for a class. The mutating keyword is only used by structures and enumerations.(class에서는 본래 mutating을 사용하지 않는다.)

protocol Togglable {
   mutating func toggle()
}

enum OnOffSwitch: Togglable {
   case off, on
   mutating func toggle() {
       switch self {
       case .off:
           self = .on
       case .on:
           self = .off
       }
   }
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
// lightSwitch is now equal to .on

Initializer Requirements

Protocols can require specific initializers to be implemented by conforming types. You write these initializers as part of the protocol’s definition in exactly the same way as for normal initializers, but without curly braces or an initializer body:

protocol SomeProtocol {
   init(someParameter: Int)
}

Class Implementations of Protocol Initializer Requirements

You can implement a protocol initializer requirement on a conforming class as either a designated initializer or a convenience initializer. In both cases, you must mark the initializer implementation with the required modifier:

class SomeClass: SomeProtocol {
   required init(someParameter: Int) {
       // initializer implementation goes here
   }
}

The use of the required modifier ensures that you provide an explicit or inherited implementation of the initializer requirement on all subclasses of the conforming class, such that they also conform to the protocol.

For more information on required initializers, see Required Initializers.

NOTE

You don’t need to mark protocol initializer implementations with the required modifier on classes that are marked with the final modifier, because final classes can’t subclassed. For more on the final modifier, see Preventing Overrides.

If a subclass overrides a designated initializer from a superclass, and also implements a matching initializer requirement from a protocol, mark the initializer implementation with both the required and overridemodifiers:

protocol SomeProtocol {
   init()
}
class SomeSuperClass {
   init() {
       // initializer implementation goes here
   }
}
class SomeSubClass: SomeSuperClass, SomeProtocol {
   // “required” from SomeProtocol conformance; “override” from SomeSuperClass
   required override init() {
       // initializer implementation goes here
   }
}

Failable Initializer Requirements

Protocols can define failable initializer requirements for conforming types, as defined in Failable Initializers.

A failable initializer requirement can be satisfied by a failable or nonfailable initializer on a conforming type. A nonfailable initializer requirement can be satisfied by a nonfailable initializer or an implicitly unwrapped failable initializer.

Protocols as Types

protocol is a type, you can use a protocol in many places where other types are allowed, including:

  • As a parameter type or return type in a function, method, or initializer
  • As the type of a constant, variable, or property
  • As the type of items in an array, dictionary, or other container

NOTE

Because protocols are types, begin their names with a capital letter 

class Dice {
   let sides: Int
   let generator: RandomNumberGenerator
   init(sides: Int, generator: RandomNumberGenerator) {
       self.sides = sides
       self.generator = generator
   }
   func roll() -> Int {
       return Int(generator.random() * Double(sides)) + 1
   }
}

This example defines a new class called Dice, which represents an n-sided dice for use in a board game. Dice instances have an integer property called sides, which represents how many sides they have, and a property called generator, which provides a random number generator from which to create dice roll values.

The generator property is of type RandomNumberGenerator. Therefore, you can set it to an instance of any type that adopts the RandomNumberGenerator protocol. Nothing else is required of the instance you assign to this property, except that the instance must adopt the RandomNumberGenerator protocol.

Here’s how the Dice class can be used to create a six-sided dice with a LinearCongruentialGeneratorinstance as its random number generator:

var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
for _ in 1…5 {
   print(“Random dice roll is (d6.roll())”)
}
// Random dice roll is 3
// Random dice roll is 5
// Random dice roll is 4
// Random dice roll is 5
// Random dice roll is 4

Delegation

Delegation is a design pattern that enables a class or structure to hand off (or delegate) some of its responsibilities to an instance of another type. This design pattern is implemented by defining a protocol that encapsulates the delegated responsibilities, such that a conforming type (known as a delegate) is guaranteed to provide the functionality that has been delegated. Delegation can be used to respond to a particular action, or to retrieve data from an external source without needing to know the underlying type of that source.

The example below defines two protocols for use with dice-based board games:

protocol DiceGame {
   var dice: Dice { get }
   func play()
}
protocol DiceGameDelegate {
   func gameDidStart(_ game: DiceGame)
   func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
   func gameDidEnd(_ game: DiceGame)
}

class SnakesAndLadders: DiceGame {
   let finalSquare = 25
   let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
   var square = 0
   var board: [Int]
   init() {
       board = Array(repeating: 0, count: finalSquare + 1)
       board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
       board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
   }
   var delegate: DiceGameDelegate?
   func play() {
       square = 0
       delegate?.gameDidStart(self)
       gameLoop: while square != finalSquare {
           let diceRoll = dice.roll()
           delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
           switch square + diceRoll {
           case finalSquare:
               break gameLoop
           case let newSquare where newSquare > finalSquare:
               continue gameLoop
           default:
               square += diceRoll
               square += board[square]
           }
       }
       delegate?.gameDidEnd(self)
   }
}

class DiceGameTracker: DiceGameDelegate {
   var numberOfTurns = 0
   func gameDidStart(_ game: DiceGame) {
       numberOfTurns = 0
       if game is SnakesAndLadders {
           print(“Started a new game of Snakes and Ladders”)
       }
       print(“The game is using a (game.dice.sides)-sided dice”)
   }
   func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
       numberOfTurns += 1
       print(“Rolled a (diceRoll)”)
   }
   func gameDidEnd(_ game: DiceGame) {
       print(“The game lasted for (numberOfTurns) turns”)
   }
}

let tracker = DiceGameTracker()
let game = SnakesAndLadders()
game.delegate = tracker
game.play()
// Started a new game of Snakes and Ladders
// The game is using a 6-sided dice
// Rolled a 3
// Rolled a 5
// Rolled a 4
// Rolled a 5
// The game lasted for 4 turns

Adding Protocol Conformance with an Extension

You can extend an existing type to adopt and conform to a new protocol, even if you don’t have access to the source code for the existing type. Extensions can add new properties, methods, and subscripts to an existing type, and are therefore able to add any requirements that a protocol may demand. For more about extensions, see Extensions.

NOTE

Existing instances of a type automatically adopt and conform to a protocol when that conformance is added to the instance’s type in an extension.

protocol TextRepresentable {
   var textualDescription: String { get }
}

extension Dice: TextRepresentable {
   var textualDescription: String {
       return “A (sides)-sided dice”
   }
}

let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
print(d12.textualDescription)
// Prints “A 12-sided dice”

extension SnakesAndLadders: TextRepresentable {
   var textualDescription: String {
       return “A game of Snakes and Ladders with (finalSquare) squares”
   }
}
print(game.textualDescription)
// Prints “A game of Snakes and Ladders with 25 squares”

Declaring Protocol Adoption with an Extension

If a type already conforms to all of the requirements of a protocol, but has not yet stated that it adopts that protocol, you can make it adopt the protocol with an empty extension:

struct Hamster {
   var name: String
   var textualDescription: String {
       return “A hamster named (name)”
   }
}

extension Hamster: TextRepresentable {}

Instances of Hamster can now be used wherever TextRepresentable is the required type:

let simonTheHamster = Hamster(name: “Simon”)
let somethingTextRepresentable: TextRepresentable = simonTheHamster
print(somethingTextRepresentable.textualDescription)
// Prints “A hamster named Simon”

NOTE

Types don’t automatically adopt a protocol just by satisfying its requirements. They must always explicitly declare their adoption of the protocol.

Collections of Protocol Types

A protocol can be used as the type to be stored in a collection such as an array or a dictionary, as mentioned in Protocols as Types. This example creates an array of TextRepresentable things:

let things: [TextRepresentable] = [game, d12, simonTheHamster]

for thing in things {
   print(thing.textualDescription)
}
// A game of Snakes and Ladders with 25 squares
// A 12-sided dice
// A hamster named Simon

Note that the thing constant is of type TextRepresentable. It’s not of type Dice, or DiceGame, or Hamster.

Protocol Inheritance

A protocol can inherit one or more other protocols and can add further requirements on top of the requirements it inherits. The syntax for protocol inheritance is similar to the syntax for class inheritance, but with the option to list multiple inherited protocols, separated by commas: (class의 경우는 하나의 super class만을 inheriting할수있지만 protocol은 여러개의 protocol을 inherit 할수있다.)

protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
   // protocol definition goes here
}

protocol PrettyTextRepresentable: TextRepresentable {
   var prettyTextualDescription: String { get }
}

This example defines a new protocol, PrettyTextRepresentable, which inherits from TextRepresentable. Anything that adopts PrettyTextRepresentable must satisfy all of the requirements enforced by TextRepresentable, plus the additional requirements enforced by PrettyTextRepresentable. In this example, PrettyTextRepresentable adds a single requirement to provide a gettable property called prettyTextualDescription that returns a String.

extension SnakesAndLadders: PrettyTextRepresentable {
   var prettyTextualDescription: String {
       var output = textualDescription + “:n”
       for index in 1…finalSquare {
           switch board[index] {
           case let ladder where ladder > 0:
               output += “▲ ”
           case let snake where snake < 0:
               output += “▼ ”
           default:
               output += “○ ”
           }
       }
       return output
   }
}

print(game.prettyTextualDescription)
// A game of Snakes and Ladders with 25 squares:
// ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○

Class-Only Protocols

(structure,enumeration에서는 conform하지 못하고 class에서만 

conform할수 잇게 만드는 방법)

You can limit protocol adoption to class types (and not structures or enumerations) by adding the AnyObjectprotocol to a protocol’s inheritance list.

protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
   // class-only protocol definition goes here
}

In the example above, SomeClassOnlyProtocol can only be adopted by class types. It’s a compile-time error to write a structure or enumeration definition that tries to adopt SomeClassOnlyProtocol.

NOTE

Use a class-only protocol when the behavior defined by that protocol’s requirements assumes or requires that a conforming type has reference semantics rather than value semantics. For more on reference and value semantics, see Structures and Enumerations Are Value Types and Classes Are Reference Types.

Protocol Composition

(두개이상의  protocol을 수행하는 어떤 타입을 표현한다.  예를 들어 a & b는 a와 b protocol을 conform하는 어떤 type 을 의미)

It can be useful to require a type to conform to multiple protocols at the same time. You can combine multiple protocols into a single requirement with a protocol composition. Protocol compositions behave as if you defined a temporary local protocol that has the combined requirements of all protocols in the composition. Protocol compositions don’t define any new protocol types.

Protocol compositions have the form SomeProtocol & AnotherProtocol. You can list as many protocols as you need, separating them with ampersands (&). In addition to its list of protocols, a protocol composition can also contain one class type, which you can use to specify a required superclass.

protocol Named {
   var name: String { get }
}
protocol Aged {
   var age: Int { get }
}
struct Person: Named, Aged {
   var name: String
   var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
   print(“Happy birthday, (celebrator.name), you’re (celebrator.age)!”)
}
let birthdayPerson = Person(name: “Malcolm”, age: 21)
wishHappyBirthday(to: birthdayPerson)
// Prints “Happy birthday, Malcolm, you’re 21!”

In this example, the Named protocol has a single requirement for a gettable String property called name. The Aged protocol has a single requirement for a gettable Int property called age. Both protocols are adopted by a structure called Person.

The example also defines a wishHappyBirthday(to:) function. The type of the celebrator parameter is Named & Aged, which means “any type that conforms to both the Named and Aged protocols.” It doesn’t matter which specific type is passed to the function, as long as it conforms to both of the required protocols.

class Location {
   var latitude: Double
   var longitude: Double
   init(latitude: Double, longitude: Double) {
       self.latitude = latitude
       self.longitude = longitude
   }
}
class City: Location, Named {
   var name: String
   init(name: String, latitude: Double, longitude: Double) {
       self.name = name
       super.init(latitude: latitude, longitude: longitude)
   }
}
func beginConcert(in location: Location & Named) {
   print(“Hello, (location.name)!”)
}
let seattle = City(name: “Seattle”, latitude: 47.6, longitude: -122.3)
beginConcert(in: seattle)
// Prints “Hello, Seattle!”

Checking for Protocol Conformance

You can use the is and as operators described in Type Casting to check for protocol conformance, and to cast to a specific protocol. Checking for and casting to a protocol follows exactly the same syntax as checking for and casting to a type:

  • The is operator returns true if an instance conforms to a protocol and returns false if it doesn’t.
  • The as? version of the downcast operator returns an optional value of the protocol’s type, and this value is nil if the instance doesn’t conform to that protocol.
  • The as! version of the downcast operator forces the downcast to the protocol type and triggers a runtime error if the downcast doesn’t succeed.

protocol HasArea {
   var area: Double { get }
}

class Circle: HasArea {
   let pi = 3.1415927
   var radius: Double
   var area: Double { return pi * radius * radius }
   init(radius: Double) { self.radius = radius }
}

class Country: HasArea {
   var area: Double
   init(area: Double) { self.area = area }
}

class Animal {
   var legs: Int
   init(legs: Int) { self.legs = legs }
}

let objects: [AnyObject] = [
   Circle(radius: 2.0),
   Country(area: 243_610),
   Animal(legs: 4)
]

for object in objects {
   if let objectWithArea = object as? HasArea {
       print(“Area is (objectWithArea.area)”)
   } else {
       print(“Something that doesn’t have an area”)
   }
}
// Area is 12.5663708
// Area is 243610.0
// Something that doesn’t have an area

Protocol Extensions

(protocol을 extension해서 이 protocol을 conform하는 모든 class,structure,enumeration이 특정 functions, properties를 기본적으로 가지게 하는 방법)

Protocols can be extended to provide method and property implementations to conforming types. This allows you to define behavior on protocols themselves, rather than in each type’s individual conformance or in a global function.

extension RandomNumberGenerator {
   func randomBool() -> Bool {
       return random() > 0.5
   }
}

By creating an extension on the protocol, all conforming types automatically gain this method implementation without any additional modification.

let generator = LinearCongruentialGenerator()
print(“Here’s a random number: (generator.random())”)
// Prints “Here’s a random number: 0.37464991998171”
print(“And here’s a random Boolean: (generator.randomBool())”)
// Prints “And here’s a random Boolean: true”

Providing Default Implementations

You can use protocol extensions to provide a default implementation to any method or computed property requirement of that protocol. If a conforming type provides its own implementation of a required method or property, that implementation will be used instead of the one provided by the extension.

NOTE

Protocol requirements with default implementations provided by extensions are distinct from optional protocol requirements. Although conforming types don’t have to provide their own implementation of either, requirements with default implementations can be called without optional chaining.

Adding Constraints to Protocol Extensions

When you define a protocol extension, you can specify constraints that conforming types must satisfy before the methods and properties of the extension are available. You write these constraints after the name of the protocol you’re extending using a generic where clause, as described in Generic Where Clauses.

For instance, you can define an extension to the Collection protocol that applies to any collection whose elements conform to the TextRepresentable protocol from the example above.

extension Collection where Iterator.Element: TextRepresentable {
   var textualDescription: String {
       let itemsAsText = self.map { $0.textualDescription }
       return “[” + itemsAsText.joined(separator: “, ”) + “]”
   }
}

The textualDescription property returns the textual description of the entire collection by concatenating the textual representation of each element in the collection into a comma-separated list, enclosed in brackets.

Consider the Hamster structure from before, which conforms to the TextRepresentable protocol, and an array of Hamster values:

let murrayTheHamster = Hamster(name: “Murray”)
let morganTheHamster = Hamster(name: “Morgan”)
let mauriceTheHamster = Hamster(name: “Maurice”)
let hamsters = [murrayTheHamster, morganTheHamster, mauriceTheHamster]

Because Array conforms to Collection and the array’s elements conform to the TextRepresentable protocol, the array can use the textualDescription property to get a textual representation of its contents:

print(hamsters.textualDescription)
// Prints “[A hamster named Murray, A hamster named Morgan, A hamster named Maurice]”

NOTE

If a conforming type satisfies the requirements for multiple constrained extensions that provide implementations for the same method or property, Swift will use the implementation corresponding to the most specialized constraints.

Extensions

Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code (known as retroactive modeling). 

Extensions in Swift can:

  • Add computed instance properties and computed type properties (stored property는 추가 할수 없다.)
  • Define instance methods and type methods
  • Provide new initializers
  • Define subscripts
  • Define and use new nested types
  • Make an existing type conform to a protocol

In Swift, you can even extend a protocol to provide implementations of its requirements or add additional functionality that conforming types can take advantage of. For more details, see Protocol Extensions.

NOTE

Extensions can add new functionality to a type, but they cannot override existing functionality.

Extension Syntax

Declare extensions with the extension keyword:

extension SomeType {
   // new functionality to add to SomeType goes here
}

An extension can extend an existing type to make it adopt one or more protocols. To add protocol conformance, you write the protocol names the same way as you write them for a class or structure:

extension SomeType: SomeProtocol, AnotherProtocol {
   // implementation of protocol requirements goes here
}

Adding protocol conformance in this way is described in Adding Protocol Conformance with an Extension.

An extension can be used to extend an existing generic type, as described in Extending a Generic Type. You can also extend a generic type to conditionally add functionality, as described in Extensions with a Generic Where Clause.

NOTE

If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined

Computed Properties

Extensions can add computed instance properties and computed type properties to existing types. This example adds five computed instance properties to Swift’s built-in Double type, to provide basic support for working with distance units:

extension Double {
   var km: Double { return self * 1_000.0 }
   var m: Double { return self }
   var cm: Double { return self / 100.0 }
   var mm: Double { return self / 1_000.0 }
   var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print(“One inch is (oneInch) meters”)
// Prints “One inch is 0.0254 meters”
let threeFeet = 3.ft
print(“Three feet is (threeFeet) meters”)
// Prints “Three feet is 0.914399970739201 meters”

let aMarathon = 42.km + 195.m
print(“A marathon is (aMarathon) meters long”)
// Prints “A marathon is 42195.0 meters long”

NOTE

Extensions can add new computed properties, but they cannot add stored properties, or add property observers to existing properties.

Initializers

Extensions can add new initializers to existing types. 

Extensions can add new convenience initializers to a class, but they cannot add new designated initializers or deinitializers to a class. Designated initializers and deinitializers must always be provided by the original class implementation.

NOTE

If you use an extension to add an initializer to a value type that provides default values for all of its stored properties and does not define any custom initializers, you can call the default initializer and memberwise initializer for that value type from within your extension’s initializer.

This would not be the case if you had written the initializer as part of the value type’s original implementation, as described in Initializer Delegation for Value Types.

struct Size {
   var width = 0.0, height = 0.0
}
struct Point {
   var x = 0.0, y = 0.0
}
struct Rect {
   var origin = Point()
   var size = Size()
}

let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
                         size: Size(width: 5.0, height: 5.0))

You can extend the Rect structure to provide an additional initializer that takes a specific center point and size:

extension Rect {
   init(center: Point, size: Size) {
       let originX = center.x – (size.width / 2)
       let originY = center.y – (size.height / 2)
       self.init(origin: Point(x: originX, y: originY), size: size)
   }
}

This new initializer starts by calculating an appropriate origin point based on the provided center point and size value. The initializer then calls the structure’s automatic memberwise initializer init(origin:size:), which stores the new origin and size values in the appropriate properties:

let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
                     size: Size(width: 3.0, height: 3.0))
// centerRect’s origin is (2.5, 2.5) and its size is (3.0, 3.0)

Methods

Extensions can add new instance methods and type methods to existing types. The following example adds a new instance method called repetitions to the Int type:

extension Int {
   func repetitions(task: () -> Void) {

      // 0과 self사이의 정수를 인덱스로 순환

      // 참고) https://stackoverflow.com/a/26083896

       for _ in 0..<self {
           task()
       }
   }
}

3.repetitions {
   print(“Hello!”)
}
// Hello!
// Hello!
// Hello!

Mutating Instance Methods

(참고사항 tumblr #swift #mutating #enum: 

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

Instance methods added with an extension can also modify (or mutate) the instance itself. Structure and enumeration methods that modify self or its properties must mark the instance method as mutating, just like mutating methods from an original implementation.

extension Int {
   mutating func square() {
       self = self * self
   }
}
var someInt = 3
someInt.square()
// someInt is now 9

Subscripts

Extensions can add new subscripts to an existing type. 

123456789[0] returns 9

123456789[1] returns 8

extension Int {
   subscript(digitIndex: Int) -> Int {
       var decimalBase = 1
       for _ in 0..<digitIndex {
           decimalBase *= 10
       }
       return (self / decimalBase) % 10
   }
}
746381295[0]
// returns 5
746381295[1]
// returns 9
746381295[2]
// returns 2
746381295[8]
// returns 7

746381295[9]
// returns 0, as if you had requested:
0746381295[9]

Nested Types

Extensions can add new nested types to existing classes, structures, and enumerations:

extension Int {
   enum Kind {
       case negative, zero, positive
   }
   var kind: Kind {
       switch self {
       case 0:
           return .zero
       case let x where x > 0:
           return .positive
       default:
           return .negative
       }
   }
}

func printIntegerKinds(_ numbers: [Int]) {
   for number in numbers {
       switch number.kind {
       case .negative:
           print(“- ”, terminator: “”)
       case .zero:
           print(“0 ”, terminator: “”)
       case .positive:
           print(“+ ”, terminator: “”)
       }
   }
   print(“”)
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
// Prints “+ + – 0 – 0 + ”

NOTE

number.kind is already known to be of type Int.Kind. Because of this, all of the Int.Kind case values can be written in shorthand form inside the switch statement, such as .negative rather than Int.Kind.negative

Extensions

Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code (known as retroactive modeling). 

Extensions in Swift can:

  • Add computed instance properties and computed type properties (stored property는 추가 할수 없다.)
  • Define instance methods and type methods
  • Provide new initializers
  • Define subscripts
  • Define and use new nested types
  • Make an existing type conform to a protocol

In Swift, you can even extend a protocol to provide implementations of its requirements or add additional functionality that conforming types can take advantage of. For more details, see Protocol Extensions.

NOTE

Extensions can add new functionality to a type, but they cannot override existing functionality.

Extension Syntax

Declare extensions with the extension keyword:

extension SomeType {
   // new functionality to add to SomeType goes here
}

An extension can extend an existing type to make it adopt one or more protocols. To add protocol conformance, you write the protocol names the same way as you write them for a class or structure:

extension SomeType: SomeProtocol, AnotherProtocol {
   // implementation of protocol requirements goes here
}

Adding protocol conformance in this way is described in Adding Protocol Conformance with an Extension.

An extension can be used to extend an existing generic type, as described in Extending a Generic Type. You can also extend a generic type to conditionally add functionality, as described in Extensions with a Generic Where Clause.

NOTE

If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined

Computed Properties

Extensions can add computed instance properties and computed type properties to existing types. This example adds five computed instance properties to Swift’s built-in Double type, to provide basic support for working with distance units:

extension Double {
   var km: Double { return self * 1_000.0 }
   var m: Double { return self }
   var cm: Double { return self / 100.0 }
   var mm: Double { return self / 1_000.0 }
   var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print(“One inch is (oneInch) meters”)
// Prints “One inch is 0.0254 meters”
let threeFeet = 3.ft
print(“Three feet is (threeFeet) meters”)
// Prints “Three feet is 0.914399970739201 meters”

let aMarathon = 42.km + 195.m
print(“A marathon is (aMarathon) meters long”)
// Prints “A marathon is 42195.0 meters long”

NOTE

Extensions can add new computed properties, but they cannot add stored properties, or add property observers to existing properties.

Initializers

Extensions can add new initializers to existing types. 

Extensions can add new convenience initializers to a class, but they cannot add new designated initializers or deinitializers to a class. Designated initializers and deinitializers must always be provided by the original class implementation.

NOTE

If you use an extension to add an initializer to a value type that provides default values for all of its stored properties and does not define any custom initializers, you can call the default initializer and memberwise initializer for that value type from within your extension’s initializer.

This would not be the case if you had written the initializer as part of the value type’s original implementation, as described in Initializer Delegation for Value Types.

struct Size {
   var width = 0.0, height = 0.0
}
struct Point {
   var x = 0.0, y = 0.0
}
struct Rect {
   var origin = Point()
   var size = Size()
}

let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
                         size: Size(width: 5.0, height: 5.0))

You can extend the Rect structure to provide an additional initializer that takes a specific center point and size:

extension Rect {
   init(center: Point, size: Size) {
       let originX = center.x – (size.width / 2)
       let originY = center.y – (size.height / 2)
       self.init(origin: Point(x: originX, y: originY), size: size)
   }
}

This new initializer starts by calculating an appropriate origin point based on the provided center point and size value. The initializer then calls the structure’s automatic memberwise initializer init(origin:size:), which stores the new origin and size values in the appropriate properties:

let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
                     size: Size(width: 3.0, height: 3.0))
// centerRect’s origin is (2.5, 2.5) and its size is (3.0, 3.0)

Methods

Extensions can add new instance methods and type methods to existing types. The following example adds a new instance method called repetitions to the Int type:

extension Int {
   func repetitions(task: () -> Void) {

      // 0과 self사이의 정수를 인덱스로 순환

      // 참고) https://stackoverflow.com/a/26083896

       for _ in 0..<self {
           task()
       }
   }
}

3.repetitions {
   print(“Hello!”)
}
// Hello!
// Hello!
// Hello!

Mutating Instance Methods

(참고사항 tumblr #swift #mutating #enum: 

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

Instance methods added with an extension can also modify (or mutate) the instance itself. Structure and enumeration methods that modify self or its properties must mark the instance method as mutating, just like mutating methods from an original implementation.

extension Int {
   mutating func square() {
       self = self * self
   }
}
var someInt = 3
someInt.square()
// someInt is now 9

Subscripts

Extensions can add new subscripts to an existing type. 

123456789[0] returns 9

123456789[1] returns 8

extension Int {
   subscript(digitIndex: Int) -> Int {
       var decimalBase = 1
       for _ in 0..<digitIndex {
           decimalBase *= 10
       }
       return (self / decimalBase) % 10
   }
}
746381295[0]
// returns 5
746381295[1]
// returns 9
746381295[2]
// returns 2
746381295[8]
// returns 7

746381295[9]
// returns 0, as if you had requested:
0746381295[9]

Nested Types

Extensions can add new nested types to existing classes, structures, and enumerations:

extension Int {
   enum Kind {
       case negative, zero, positive
   }
   var kind: Kind {
       switch self {
       case 0:
           return .zero
       case let x where x > 0:
           return .positive
       default:
           return .negative
       }
   }
}

func printIntegerKinds(_ numbers: [Int]) {
   for number in numbers {
       switch number.kind {
       case .negative:
           print(“- ”, terminator: “”)
       case .zero:
           print(“0 ”, terminator: “”)
       case .positive:
           print(“+ ”, terminator: “”)
       }
   }
   print(“”)
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
// Prints “+ + – 0 – 0 + ”

NOTE

number.kind is already known to be of type Int.Kind. Because of this, all of the Int.Kind case values can be written in shorthand form inside the switch statement, such as .negative rather than Int.Kind.negative

Designated Initializers and Convenience Initializers in Swift

Designated Initializers and Convenience Initializers in Swift

Properties

Properties associate values with a particular class, structure, or enumeration. Stored properties store constant and variable values as part of an instance, whereas computed properties calculate (rather than store) a value. Computed properties are provided by classes, structures, and enumerations. Stored properties are provided only by classes and structures.

Stored and computed properties are usually associated with instances of a particular type. However, properties can also be associated with the type itself. Such properties are known as type properties.

In addition, you can define property observers to monitor changes in a property’s value, which you can respond to with custom actions. Property observers can be added to stored properties you define yourself, and also to properties that a subclass inherits from its superclass.

Stored Properties

Stored properties can be either variable stored properties (introduced by the varkeyword) or constant stored properties (introduced by the let keyword).

You can provide a default value for a stored property as part of its definition, as described in Default Property Values. You can also set and modify the initial value for a stored property during initialization. This is true even for constant stored properties, as described in Assigning Constant Properties During Initialization.

struct FixedLengthRange {
   var firstValue: Int
   let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// the range represents integer values 0, 1, and 2
rangeOfThreeItems.firstValue = 6
// the range now represents integer values 6, 7, and 8

let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// this range represents integer values 0, 1, 2, and 3
rangeOfFourItems.firstValue = 6
// this will report an error, even though firstValue is a variable property

This behavior is due to structures being value types. When an instance of a value type is marked as a constant, so are all of its properties.

The same is not true for classes, which are reference types. If you assign an instance of a reference type to a constant, you can still change that instance’s variable properties.

Stored Properties of Constant Structure Instances

If you create an instance of a structure and assign that instance to a constant, you cannot modify the instance’s properties, even if they were declared as variable properties:

let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// this range represents integer values 0, 1, 2, and 3
rangeOfFourItems.firstValue = 6
// this will report an error, even though firstValue is a variable property

This behavior is due to structures being value types. When an instance of a value type is marked as a constant, so are all of its properties.

The same is not true for classes, which are reference types. If you assign an instance of a reference type to a constant, you can still change that instance’s variable properties.

Lazy Stored Properties

A lazy stored property is a property whose initial value is not calculated until the first time it is used. You indicate a lazy stored property by writing the lazy modifier before its declaration.

NOTE

You must always declare a lazy property as a variable (with the var keyword), because its initial value might not be retrieved until after instance initialization completes. Constant properties must always have a value before initialization completes, and therefore cannot be declared as lazy.

class DataImporter {
   /*
    DataImporter is a class to import data from an external file.
    The class is assumed to take a nontrivial amount of time to initialize.
    */
   var filename = “data.txt”
   // the DataImporter class would provide data importing functionality here
}
class DataManager {
   lazy var importer = DataImporter()
   var data = [String]()
   // the DataManager class would provide data management functionality here
}
let manager = DataManager()
manager.data.append(“Some data”)
manager.data.append(“Some more data”)
// the DataImporter instance for the importer property has not yet been created

Because it is marked with the lazy modifier, the DataImporter instance for the importer property is only created when the importer property is first accessed, such as when its filename property is queried:

print(manager.importer.filename)
// the DataImporter instance for the importer property has now been created
// Prints “data.txt”

NOTE

If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once.

Computed Properties

getter and an optional setter to retrieve and set other properties and values indirectly.

struct Point {
   var x = 0.0, y = 0.0
}
struct Size {
   var width = 0.0, height = 0.0
}
struct Rect {
   var origin = Point()
   var size = Size()
   var center: Point {
       get {
           let centerX = origin.x + (size.width / 2)
           let centerY = origin.y + (size.height / 2)
           return Point(x: centerX, y: centerY)
       }
       set(newCenter) {
           origin.x = newCenter.x – (size.width / 2)
           origin.y = newCenter.y – (size.height / 2)
       }
   }
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
                 size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print(“square.origin is now at ((square.origin.x), (square.origin.y))”)
// Prints “square.origin is now at (10.0, 10.0)”

Shorthand Setter Declaration

If a computed property’s setter does not define a name for the new value to be set, a default name of newValueis used. 

struct AlternativeRect {
   var origin = Point()
   var size = Size()
   var center: Point {
       get {
           let centerX = origin.x + (size.width / 2)
           let centerY = origin.y + (size.height / 2)
           return Point(x: centerX, y: centerY)
       }

       // set(사용될변수이름) 

      //  이런 형태이나 아래와 같이 축약형으로사용 가능 newValue는 swift가          //  제공하는 기본 변수이름
       set {
           origin.x = newValue.x – (size.width / 2)
           origin.y = newValue.y – (size.height / 2)
       }
   }
}

Read-Only Computed Properties

A computed property with a getter but no setter is known as a read-only computed property. A read-only computed property always returns a value, and can be accessed through dot syntax, but cannot be set to a different value.

NOTE

You must declare computed properties—including read-only computed properties—as variable properties with the var keyword, because their value is not fixed. The let keyword is only used for constant properties, to indicate that their values cannot be changed once they are set as part of instance initialization.

struct Cuboid {
   var width = 0.0, height = 0.0, depth = 0.0
   var volume: Double {
       return width * height * depth
   }
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print(“the volume of fourByFiveByTwo is (fourByFiveByTwo.volume)”)
// Prints “the volume of fourByFiveByTwo is 40.0”

Property Observers

Property observers are called every time a property’s value is set, even if the new value is the same as the property’s current value.

You can add property observers to any stored properties you define, except for lazy stored properties. You can also add property observers to any inherited property (whether stored or computed) by overriding the property within a subclass. You don’t need to define property observers for nonoverridden computed properties, because you can observe and respond to changes to their value in the computed property’s setter. Property overriding is described in Overriding.

You have the option to define either or both of these observers on a property:

  • willSet is called just before the value is stored.
  • didSet is called immediately after the new value is stored.

If you implement a willSet observer, it’s passed the new property value as a constant parameter. You can specify a name for this parameter as part of your willSet implementation. If you don’t write the parameter name and parentheses within your implementation, the parameter is made available with a default parameter name of newValue.

Similarly, if you implement a didSet observer, it’s passed a constant parameter containing the old property value. You can name the parameter or use the default parameter name of oldValue. If you assign a value to a property within its own didSet observer, the new value that you assign replaces the one that was just set.

NOTE

The willSet and didSet observers of superclass properties are called when a property is set in a subclass initializer, after the superclass initializer has been called. They are not called while a class is setting its own properties, before the superclass initializer has been called.

class StepCounter {
   var totalSteps: Int = 0 {
       willSet(newTotalSteps) {
           print(“About to set totalSteps to (newTotalSteps)”)
       }
       didSet {
           if totalSteps > oldValue  {
               print(“Added (totalSteps – oldValue) steps”)
           }
       }
   }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps

NOTE

If you pass a property that has observers to a function as an in-out parameter, the willSet and didSet observers are always called. This is because of the copy-in copy-out memory model for in-out parameters: The value is always written back to the property at the end of the function. For a detailed discussion of the behavior of in-out parameters, see In-Out Parameters.

Global and Local Variables

The capabilities described above for computing and observing properties are also available to global variables and local variables. Global variables are variables that are defined outside of any function, method, closure, or type context. Local variables are variables that are defined within a function, method, or closure context.

The global and local variables you have encountered in previous chapters have all been stored variables. Stored variables, like stored properties, provide storage for a value of a certain type and allow that value to be set and retrieved.

However, you can also define computed variables and define observers for stored variables, in either a global or local scope. Computed variables calculate their value, rather than storing it, and they are written in the same way as computed properties.

NOTE

Global constants and variables are always computed lazily, in a similar manner to Lazy Stored Properties. Unlike lazy stored properties, global constants and variables do not need to be marked with the lazy modifier.

Local constants and variables are never computed lazily.

Type Properties

Instance properties are properties that belong to an instance of a particular type. 

You can also define properties that belong to the type itself, not to any one instance of that type. There will only ever be one copy of these properties.

Type properties are useful for defining values that are universal to all instances of a particular type, such as a constant property that all instances can use (like a static constant in C), or a variable property that stores a value that is global to all instances of that type (like a static variable in C).

Stored type properties can be variables or constants. Computed type properties are always declared as variable properties, in the same way as computed instance properties.

NOTE

Unlike stored instance properties, you must always give stored type properties a default value. This is because the type itself does not have an initializer that can assign a value to a stored type property at initialization time.

Stored type properties are lazily initialized on their first access. They are guaranteed to be initialized only once, even when accessed by multiple threads simultaneously, and they do not need to be marked with the lazy modifier.

Type Property Syntax

type properties are written as part of the type’s definition, within the type’s outer curly braces, and each type property is explicitly scoped to the type it supports.

You define type properties with the static keyword. For computed type properties for class types, you can use the class keyword instead to allow subclasses to override the superclass’s implementation. The example below shows the syntax for stored and computed type properties:

struct SomeStructure {
   static var storedTypeProperty = “Some value.”
   static var computedTypeProperty: Int {
       return 1
   }
}
enum SomeEnumeration {
   static var storedTypeProperty = “Some value.”
   static var computedTypeProperty: Int {
       return 6
   }
}
class SomeClass {
   static var storedTypeProperty = “Some value.”
   static var computedTypeProperty: Int {
       return 27
   }
   class var overrideableComputedTypeProperty: Int {
       return 107
   }
}

NOTE

The computed type property examples above are for read-only computed type properties, but you can also define read-write computed type properties with the same syntax as for computed instance properties.

Querying and Setting Type Properties

(type property는 클래스에 존재하는 상수,변수이며 이는 클래스이름을 통해 공유된다.)

Type properties are queried and set with dot syntax, just like instance properties. However, type properties are queried and set on the type, not on an instance of that type. For example:

print(SomeStructure.storedTypeProperty)
// Prints “Some value.”
SomeStructure.storedTypeProperty = “Another value.”
print(SomeStructure.storedTypeProperty)
// Prints “Another value.”
print(SomeEnumeration.computedTypeProperty)
// Prints “6”
print(SomeClass.computedTypeProperty)
// Prints “27”

struct AudioChannel {
   static let thresholdLevel = 10
   static var maxInputLevelForAllChannels = 0
   var currentLevel: Int = 0 {
       didSet {
           if currentLevel > AudioChannel.thresholdLevel {
               // cap the new audio level to the threshold level
               currentLevel = AudioChannel.thresholdLevel
           }
           if currentLevel > AudioChannel.maxInputLevelForAllChannels {
               // store this as the new overall maximum input level
               AudioChannel.maxInputLevelForAllChannels = currentLevel
           }
       }
   }
}

var leftChannel = AudioChannel()
var rightChannel = AudioChannel()

leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
// Prints “7”
print(AudioChannel.maxInputLevelForAllChannels)
// Prints “7”

rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
// Prints “10”
print(AudioChannel.maxInputLevelForAllChannels)
// Prints “10”

// 위에와는 달리 할당되 새로운 값이 type property에 존재하는 것을 알수 있다.