A protocol defines a blueprint that specifies the methods, attributes, and other things needed to implement a particular task or function.
Classes, structs, or enumerations can follow the protocol and provide specific implementations for those requirements defined by the Protocol, and a type that satisfies the requirements of a protocol, it can be said that the type follows this protocol
In addition to complying with the requirements of the type of protocol that must be implemented, you can extend the protocol to implement some of the extensions or implement some additional functionality, so that the types of protocols you follow can use these features
Syntax of the Protocol
Protocols are defined in much the same way as classes, structs, and enumerations.
Protocol someprotocol{
Here is the definition part of the protocol.
// }
To have a custom type adhere to a protocol, when defining a type, you need to add a protocol name after the type name, with the middle (:) segmentation, when following multiple protocols, between the protocols with (,) split
struct somestruct:firstprotocol,anotherprotocol{
Here is the definition part of the struct
// }
Class with parent class in the training protocol, the class name of the parent class should be placed before the protocol name, split
Class someclass:somesuperclass.firstprotocol,anotherprotocol{
Here is the definition part of the class
// }
Property requirements
A protocol can require instance properties or type properties that follow a type of protocol to provide a specific name and type
The protocol does not specify whether a property is a stored or computed property, it specifies only the name and type of the property, and the protocol number specifies whether the property is readable or readable writable
If the protocol requires that the property be readable and writable, then the property cannot be a constant attribute or a read-only computed property, and if the protocol requires that the property be readable, then the property is not only readable if the code requires it. It can also be writable.
The protocol always uses the VAR keyword to declare a variable property, with the type name appended with {set Get} to indicate that the property is readable and writable, and the readable attribute is declared with the {get}
Protocol someprotocol{
var mustbesettable:int {Get Set}
var dosenotneedtobesettable:int {Get}
}
When you define a type attribute in an agreement, always use the static keyword as the prefix, and when the class type follows the protocol, you can use the CLASS keyword to declare the Type property in addition to the Static keyword:
Protocol anotherprotocol{
static Var sometypeproperty:int {Get Set}
}
As follows: This is a protocol that contains only one instance property requirement:
Protocol fullnamed{
var fullname:string {Get}
}
The Fullnamed protocol, in addition to the FullName attribute that is required to comply with the type of protocol provided, does not require any other requirements, and this protocol indicates that any type that follows fullnamed must have an instance property of type String for a tick fullName
Here is a simple struct that follows the Fullnamed protocol
struct person:fullnamed{
var fullname:string
}
Let John = Person.init (fullName: "John Appleseed")
This example defines a person's structure, which is used to denote a name, which follows the fullnamed protocol.
Each instance of the person struct has a type of String storage property, FullName, which satisfies the requirements of the Fullnamed protocol, assuming that the person struct correctly conforms to the Protocol (if the protocol requirements are not fully met, the compile-time error )
Here's a more complicated example.
Class starship:fullnamed{
var prefix:string?
var name:string
Init (name:string,prefix:string?) {
Self.name = Name
Self.prefix = Prefix
}
var fullname:string{
return (prefix! = nil? prefix! + "": "") + Name
}
}
var ncc1701 = starship.init (name: "Enterprise", Prefix: "USS")
Method requirements
A protocol can require that certain specified instance methods or class methods be implemented in accordance with the type of the protocol, as part of the protocol, in the definition of the protocol as a normal method, but without braces and method bodies, which can be defined in the protocol with variable parameters, as in normal methods, but Providing default values for the parameters of the methods in the protocol is not supported
As described in the attribute requirements, when defining a class method in a protocol, always use the static keyword as a prefix, and when the class type follows Rey, you can use the Class keyword as a prefix, in addition to the static keyword.
The following example defines a protocol that contains only one instance method:
Protocol randomnumbergenerator{
Func random () Double
}
The RandomNumberGenerator protocol requires that a type that follows the protocol must have an instance method named random that returns a value of type Double. Although not specified here, we assume that the return value is within the [0.0,1.0] range
The RandomNumberGenerator protocol does not care how each random number is generated, it only requires a random number to be supplied to the living device
As shown below:
Class linearcongrentialgenerator: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 = Linearcongrentialgenerator.init ()
Print ("Here ' s a random number: \ (Generator.random ())")
Print ("and another one: \ (Generator.random ())")
Mutating method Requirements
Sometimes it is necessary to change the instance of a method in a method, for example, in an instance method of a value type (that is, struct and enum), the mutating keyword is prefixed with the method, written in front of the Func, indicating that the instance to which it belongs and the value of any property of the instance can be modified in the method.
If you define an instance method in the agreement, the method changes the instance of the type following the protocol, then the definition protocol is required to add the mutating keyword before the method, which allows the struct and enumeration to follow the protocol and satisfy this method requirement
Note: When implementing the Mutating method in the protocol, if the class type does not have to write the mutating keyword, and for structs and enumerations, you must write the Mutataing keyword
Protocol togglable{
mutating func Toggle ()
}
The toggle () method, when defined, uses the Mutating keyword tag, which indicates that when it is called, the method will change the instance of the type following the protocol.
When using enumerations or structs to implement the Togglable protocol, you need to provide a toggle () method with a mutating prefix
Enum oneoffswitch:togglable{
Case off, on
mutating func Toggle () {
Switch Self {
case. Off:
Self =. On
Case-On:
Self =. Off
}
}
}
var lightSwitch = Oneoffswitch.off
Lightswitch.toggle ()
Constructor requirements
The protocol can require that the specified constructor be implemented in accordance with the type of the protocol, and you can write the constructor's declaration in the definition of the protocol, without writing the braces and the constructor's entity, as you would write the generic constructor.
Protocol someprotocol2{
Init (Someparameter:int)
}
Constructors require implementations in a class
You can implement constructors in classes that follow the protocol, either as a constructor or as a convenience constructor, and in either case you must label the constructor implementation with the required modifier
Class someclass:someprotocol2{
Required Init (someparameter:int) {
Here is the implementation part of the constructor
}
}
Using the required modifier ensures that all subclasses must provide this constructor implementation, which can also compound the protocol
If the class has already been marked as final, then the implementation value of the constructor that does not need to be negotiated uses the required modifier, because the final class cannot have subclasses
If a subclass overrides the specified constructor of the parent class, and the constructor satisfies the requirements of a protocol, the implementation of the constructor requires that both the required and the override modifiers be annotated
Protocol SomeProtocol3 {
Init ()
}
Class SomeSuperClass3 {
Init () {
Here is the implementation part of the constructor
}
}
Class Somesubclass:somesuperclass3, SomeProtocol3 {
Because following the agreement, we need to add required
Because inheriting from the parent class, you need to add override
Required override Init () {
Here is the implementation part of the constructor
}
}
can fail constructor requirements
The protocol can also define a constructor requirement that can fail for a type that follows a protocol
Types that follow the protocol can meet the requirements of the failed constructors defined in the protocol through the Class table's constructor (init?) or the non-failed constructor (INIT)
Non-fail constructors that are defined in the protocol can fail the constructor (init!) through a non-fail constructor (init) or an implicit unpacking To meet
Protocol as type
Although the protocol itself does not implement any functionality, the protocol can be used as a mature type
Protocols can be used just like other types, using scenarios such as the following
As a function, a parameter type or a return value type in a method or struct body
As a constant, the type of the variable or property
As an array, an element type in a dictionary or other container
Note: The protocol is a type, so the name of the protocol type should be the same as other types (Int, Double, String), and camel-like notation, which begins with uppercase letters
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
}
}
Delegate (proxy) mode
A delegate is a design pattern that allows a class or struct to delegate functionality to other types of instances that require them to be responsible, and the implementation of the delegate pattern is simple: Define the protocol to encapsulate the functions that need to be delegated, so that the types that follow the protocol can provide these functions, the delegate pattern can be used to respond to a specific action, or External data sources, without caring about the type of external data sources
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)
}
The Dicegame protocol can be followed by any game involving dice, dicegamedelegate protocol can be followed by any type, used to track Dicegame's game process
as shown below.
Class sankesandladders:dicegame{
Let Finalsquare = 25
Let dice = Dice.init (Sides:6, Generator:linearcongrentialgenerator ())
var square = 0
VAR board: [Int]
Init () {
board = [Int].init (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)
}
}
The following example defines the Dicegametracker class, which follows the Dicegamedelegate protocol:
Class dicegametracker:dicegamedelegate{
var numberofturns = 0
Func Gamedidstart (_ Game:dicegame) {
Numberofturns = 0
If game is sankesandladders {
Print ("Started a new game of Snaks 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")
}
}
Swift Learning-24--Protocol 01