methods

Methods are functions associated with some particular type.

  • Classes, structures, and enumerations can all define instance methods; Instance methods encapsulate specific tasks and functions for instances of a given type.
  • Classes, structures, and enumerations can also define type methods
    • A type method is associated with the type itself
    • Type methods are similar to class methods in Objective-C
  • Structs and enumerations can define methods that areSwiftC/Objective-CIs one of the main differences. In Objective-C, a class is the only type that defines a method. But in Swift, you can not only choose whether you want to define a class/structure/enumeration, but also have the flexibility to define methods on the type (class/structure/enumeration) you create.

Instance Methods

  • Instance methods are methods that belong to a particular class, structure, or instance of an enumerated type.
  • Instance methods support instance functionality by providing methods to access and modify instance properties or by providing functionality relevant to the purpose of the instance.
  • Instance methods have exactly the same syntax as functions, see functions for details.
  • Instance methods are written before and after braces of the type to which they belong. Instance methods have implicit access to all other instance methods and properties of their type.
  • Instance methods can only be called by a specific instance of the class to which they belong. Instance methods cannot be called without an existing instance.

In the following example, we define a very simple Counter class that can be used to count the number of times an action occurs:

class Counter {
    var count = 0
    func increment(a) {
        count+ =1
    }
    func increment(by amount: Int) {
        count += amount
    }
    func reset(a) {
        count = 0}}Copy the code

The Counter class defines three instance methods:

  • incrementIncrementing the counter by one;
  • increment(by: Int)Increments the counter by a specified integer value;
  • resetReset the counter to0.

The Counter class also declares a mutable property, count, that keeps track of the current Counter value.

The instance method is called with dot syntax as if it were a property:

let counter = Counter(a)// The initial count is 0
counter.increment()
// The count is now 1
counter.increment(by: 5)
// The count is now 6
counter.reset()
// The count is now 0
Copy the code

Function parameters can have both a local name (used inside the function body) and an external name (used when the function is called). For details, see Specifying external Parameter names. The same goes for method parameters, because a method is a function that is associated with a type.

selfattribute

Each instance of a type has an implied property called self, which is exactly equivalent to the instance itself. You can use the implied self attribute in an instance’s instance method to refer to the current instance.

The increment method in the above example can also be written as:

func increment(a) {
    self.count+ =1
}
Copy the code

In fact, you don’t have to write self very often in your code. Whenever you use a known property or method name in a method, Swift assumes that you are referring to the property or method of the current instance if you do not explicitly write self. This assumption was demonstrated in Counter above: all three instance methods in Counter use count (instead of self.count).

The main scenario for using this rule is when the name of an instance method parameter is the same as the name of an instance attribute. In this case, parameter names take precedence and must be used in a stricter manner when referring to attributes. At this point you can use the self attribute to distinguish the parameter name from the attribute name.

In the following example, self disambiguates the method argument x and the instance attribute 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")}// Print "This point is to the right of the line where x == 1.0"
Copy the code

Without the self prefix, Swift assumes that both uses of x refer to a method parameter named x.

Modify the value type in the instance method

Structs and enumerations are value types. By default, properties of a value type cannot be modified in its instance methods.

However, if you do need to modify the properties of a structure or enumeration in a particular method, you can select mutating behavior for that method and then change its properties from within its method. And any changes made to this method will be written back to the original structure at the end of the method execution. A method can also give its implied self attribute an entirely new instance that replaces the existing one at the end of the method. To use mutable methods, place the keyword mutating before the func keyword of the 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))")
Print "The point is now at (3.0, 4.0)"
Copy the code

The Point structure above defines a mutable method moveBy (x: y 🙂 to move the Point instance to the given position. The method is called to modify the point, rather than returning a new point. Methods are defined with the mutating keyword to allow properties to be modified.

Note that mutable methods cannot be called on a constant of structure type because its properties cannot be changed, even if the properties are variable properties. See storage properties of a constant structure for details:

let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0)
// An error will be reported
Copy the code
Give in mutable methodsselfThe assignment

Mutable methods can assign an entirely new instance of the implicit property self. The Point example above can be rewritten as follows:

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)
    }
}
Copy the code

The new mutable method moveBy(x:y:) creates a new instance of a structure whose x and y values are set to target values. The end result of calling this version of the method is the same as the last version. Mutable methods of enumerations can set self to different members of the same enumeration type:

enum TriStateSwitch {
    case off, low, high
    mutating func next(a) {
        switch self {
        case .off:
            self = .low
        case .low:
            self = .high
        case .high:
            self = .off
        }
    }
}
var ovenLight = TriStateSwitch.low
ovenLight.next()
// ovenLight now equals.high
ovenLight.next()
// ovenLight now equals.off
Copy the code

The above example defines an enumeration of three-state toggles. Each time the next() method is called, the switch cycles between different power states (off, Low, high).


Type method

Instance methods are methods that are called by instances of a certain type. You can also define methods called on the type itself, which are called type methods. Specify a type method by adding the static keyword before the func keyword of the method. Classes can also be specified with the keyword class, allowing subclasses to override the parent class’s implementation of the method.

Pay attention to

In Objective-C, you can only define type-level methods for Objective-C classes. In Swift, you can define type methods for all classes, structures, and enumerations. Each type method is explicitly included by the type it supports.

Type methods are called with dot syntax just like instance methods. However, you are calling this method on a type, not on an instance. Here is an example of how to call a type method on a SomeClass class:

class SomeClass {
    class func someTypeMethod(a){
        // Implement type methods here}}SomeClass.someTypeMethod()
Copy the code

In the body of a type method, the self attribute refers to the type itself, not to an instance of the type. This means that you can use self to disambiguate type attributes and type method parameters (similar to what we did earlier with instance attributes and instance method parameters).

In general, any unqualified method and attribute names in the body of a type method can be referenced by other type methods and attribute names in the class. A type method can call other type methods in the class directly from the name of the type method without having to prefix the method name with the type name. Similarly, in structures and enumerations, you can access a type attribute in a class directly by its name without having to be preceded by the type name.

The following example defines a structure called LevelTracker. It monitors the player’s progress in the game (at different levels or stages of the game). It is a single player game, but can also store game information for multiple players on the same device. When the game starts, all game levels (except level 1) are locked. Every time a player completes a level, that level is unlocked for all players on that device. The LevelTracker structure uses type attributes and methods to monitor which level of the game has been unlocked. It also monitors each player’s current level.

struct LevelTracker {
    static var highestUnlockedLevel = 1
    var currentLevel = 1

    static func unlock(_ level: Int) {
        if level > highestUnlockedLevel { highestUnlockedLevel = level }
    }

    static func isUnlocked(_ level: Int) -> Bool {
        return level <= highestUnlockedLevel
    }

    @discardableResult
    mutating func advance(to level: Int) -> Bool {
        if LevelTracker.isUnlocked(level) {
            currentLevel = level
            return true
        } else {
            return false}}}Copy the code

LevelTracker monitors the highest level a player has unlocked. This value is stored in the type attribute highestUnlockedLevel.

LevelTracker also defines two type methods that work with highestUnlockedLevel. The first type method is unlock(_:), which updates the value of highestUnlockedLevel once the new level is unlocked. The second type method is isUnlocked(_:), which returns true if a given level has already been unlocked. . (note that although we didn’t use similar LevelTracker highestUnlockedLevel writing, this type of method or access type attribute highestUnlockedLevel)

In addition to type attributes and type methods, LevelTracker also monitors each player’s progress. It uses the instance property currentLevel to monitor each player’s currentLevel.

To facilitate the management of the currentLevel property, LevelTracker defines the instance method advance(to:). This method checks to see if the requested new level is unlocked before updating currentLevel. The advance(to:) method returns a Boolean value indicating whether currentLevel can be set. Because advance(to:) is allowed to ignore the return value without generating a compile warning, the function is labeled as the @discardableresult property. For more information on properties, see the features section.

Below, the Player class uses LevelTracker to monitor and update each Player’s progress:

class Player {
    var tracker = LevelTracker(a)let playerName: String
    func complete(level: Int) {
        LevelTracker.unlock(level + 1)
        tracker.advance(to: level + 1)}init(name: String) {
        playerName = name
    }
}
Copy the code

The Player class creates a new LevelTracker instance to monitor the user’s progress. It provides the complete(level:) method, which is called once the player completes a given level. This method unlocks the next level for all players and updates the current player’s progress to the next level. (we ignored the Boolean returned by advance(to:) because leveltracker.unlock (_:) was already unlocked when we called leveltracker.unlock (_:).)

You can also create an instance of Player for a new Player and see what happens when that Player completes level 1:

var player = Player(name: "Argyrios")
player.complete(level: 1)
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
// Print "Highest unlocked level is now 2".
Copy the code

If you create a second player and try to get him to start a level that has not been unlocked by any player, attempting to set the player’s current level will fail:

player = Player(name: "Beto")
if player.tracker.advance(to: 6) {
    print("player is now on level 6")}else {
    print("level 6 has not yet been unlocked")}// Print "Level 6 has not yet been unlocked".Copy the code