进击的雨燕---------------方法

方法是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法;类型方法与类型本身相关联。类型方法与 Objective-C 中的类方法(class methods)相似。

结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活地在你创建的类型(类/结构体/枚举)上定义方法。

实例方法 (Instance Methods)

实例方法是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见函数

实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。

下面的例子,定义一个很简单的Counter类,Counter能被用来对一个动作发生的次数进行计数:

class Counter {
    var count = 0
    func increment() {
        ++count
    }
    func incrementBy(amount: Int) {
        count += amount
    }
    func reset() {
        count = 0
    }
}

Counter类定义了三个实例方法:

  • increment让计数器按一递增;
  • incrementBy(amount: Int)让计数器按一个指定的整数值递增;
  • reset将计数器重置为0。

Counter这个类还声明了一个可变属性count,用它来保持对当前计数器值的追踪。

和调用属性一样,用点语法(dot syntax)调用实例方法:

let counter = Counter()
// 初始计数值是0
counter.increment()
// 计数值现在是1
counter.incrementBy(5)
// 计数值现在是6
counter.reset()
// 计数值现在是0

方法的局部参数名称和外部参数名称 (Local and External Parameter Names for Methods)

函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见指定外部参数名。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。

Swift 中的方法和 Objective-C 中的方法极其相似。像在 Objective-C 中一样,Swift 中方法的名称通常用一个介词指向方法的第一个参数,比如:withforby等等。前面的Counter类的例子中incrementBy(_:)方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。

具体来说,Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。这个约定与典型的命名和调用约定相适应,与你在写 Objective-C 的方法时很相似。这个约定还让富于表达性的方法在调用时不需要再限定参数名称。

看看下面这个Counter的另一个版本(它定义了一个更复杂的incrementBy(_:)方法):

class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, numberOfTimes: Int) {
        count += amount * numberOfTimes
    }
}

incrementBy(_:numverOfTimes:)方法有两个参数: amountnumberOfTimes。默认情况下,Swift 只把amount当作一个局部名称,但是把numberOfTimes即看作局部名称又看作外部名称。下面调用这个方法:

let counter = Counter()
counter.incrementBy(5, numberOfTimes: 3)
// counter 的值现在是 15

你不必为第一个参数值再定义一个外部变量名:因为从函数名incrementBy(_numberOfTimes:)已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。

上面描述的这种默认行为意味着在 Swift 中,定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然且富于表达性的方式被调用。

修改方法的外部参数名称(Modifying External Parameter Name Behavior for Methods)

有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你自己可以为第一个参数添加一个显式的外部名称。

相反,如果你不想为方法的第二个及后续的参数提供一个外部名称,可以通过使用下划线(_)作为该参数的显式外部名称,这样做将覆盖默认行为。

self 属性(The self Property)

类型的每一个实例都有一个隐含属性叫做selfself完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的self属性来引用当前实例。

上面例子中的increment方法还可以这样写:

func increment() {
    self.count++
}

实际上,你不必在你的代码里面经常写self。不论何时,只要在一个方法中使用一个已知的属性或者方法名称,如果你没有明确地写self,Swift 假定你是指当前实例的属性或者方法。这种假定在上面的Counter中已经示范了:Counter中的三个实例方法中都使用的是count(而不是self.count)。

使用这条规则的主要场景是实例方法的某个参数名称与实例的某个属性名称相同的时候。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更严格的方式。这时你可以使用self属性来区分参数名称和属性名称。

下面的例子中,self消除方法参数x和实例属性x之间的歧义:

struct Point {
    var x = 0.0, y = 0.0
    func isToTheRightOfX(x: Double) -> Bool {
        return self.x > x
    }
}
let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOfX(1.0) {
    print("This point is to the right of the line where x == 1.0")
}
// 打印输出: This point is to the right of the line where x == 1.0

如果不使用self前缀,Swift 就认为两次使用的x都指的是名称为x的函数参数。

在实例方法中修改值类型(Modifying Value Types from Within Instance Methods)

结构体和枚举是值类型。一般情况下,值类型的属性不能在它的实例方法中被修改。

但是,如果你确实需要在某个特定的方法中修改结构体或者枚举的属性,你可以选择变异(mutating)这个方法,然后方法就可以从方法内部改变它的属性;并且它做的任何改变在方法结束时还会保留在原始结构中。方法还可以给它隐含的self属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。

要使用变异方法,将关键字mutating 放到方法的func关键字之前就可以了:

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveByX(deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveByX(2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// 打印输出: "The point is now at (3.0, 4.0)"

上面的Point结构体定义了一个可变方法(mutating method)moveByX(_:y:)用来移动点。该方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上了mutating关键字,从而可以修改属性。

注意,不能在结构体类型的常量上调用可变方法,因为其属性不能被改变,即使属性是变量属性,详情参见常量结构体的存储属性

let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveByX(2.0, y: 3.0)
// 这里将会报告一个错误

在可变方法中给 self 赋值(Assigning to self Within a Mutating Method)

可变方法能够赋给隐含属性self一个全新的实例。上面Point的例子可以用下面的方式改写:

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

新版的可变方法moveByX(_:y:)创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。

枚举的可变方法可以把self设置为同一枚举类型中不同的成员:

enum TriStateSwitch {
    case Off, Low, High
    mutating func next() {
        switch self {
        case Off:
            self = Low
        case Low:
            self = High
        case High:
            self = Off
        }
    }
}
var ovenLight = TriStateSwitch.Low
ovenLight.next()
// ovenLight 现在等于 .High
ovenLight.next()
// ovenLight 现在等于 .Off

上面的例子中定义了一个三态开关的枚举。每次调用next()方法时,开关在不同的电源状态(OffLowHigh)之间循环切换。

类型方法 (Type Methods)

实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做类型方法。声明结构体和枚举的类型方法,在方法的func关键字之前加上关键字static。类可能会用关键字class来允许子类重写父类的方法实现。

注意
在 Objective-C 中,你只能为 Objective-C 的类定义类型方法(type-level methods)。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法。每一个类型方法都被它所支持的类型显式包含。

类型方法和实例方法一样用点语法调用。但是,你是在类型层面上调用这个方法,而不是在实例层面上调用。下面是如何在SomeClass类上调用类型方法的例子:

class SomeClass {
    static func someTypeMethod() {
        // type method implementation goes here
    }
}
SomeClass.someTypeMethod()

在类型方法的方法体(body)中,self指向这个类型本身,而不是类型的某个实例。这意味着你可以用self来消除类型属性和类型方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。

一般来说,在类型方法的方法体中,任何未限定的方法和属性名称,将会指代本类中其他类型方法和类型属性。一个类型方法可以通过类型方法的名称调用本类中的类型方法,而无需在方法名称前面加上类型名称前缀。同样,也能够直接通过类型属性的名称访问本类中的类型属性,而不需要类型名称前缀。

下面的例子定义了一个名为LevelTracker结构体。它监测玩家的游戏发展情况(游戏的不同层次或阶段)。这是一个单人游戏,但也可以存储多个玩家在同一设备上的游戏信息。

游戏初始时,所有的游戏等级(除了等级 1)都被锁定。每次有玩家完成一个等级,这个等级就对这个设备上的所有玩家解锁。LevelTracker结构体用类型属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。

struct LevelTracker {
    static var highestUnlockedLevel = 1
    static func unlockLevel(level: Int) {
        if level > highestUnlockedLevel { highestUnlockedLevel = level }
    }
    static func levelIsUnlocked(level: Int) -> Bool {
        return level <= highestUnlockedLevel
    }
    var currentLevel = 1
    mutating func advanceToLevel(level: Int) -> Bool {
        if LevelTracker.levelIsUnlocked(level) {
            currentLevel = level
            return true
        } else {
            return false
        }
    }
}

LevelTracker监测玩家已解锁的最高等级。这个值被存储在类型属性highestUnlockedLevel中。

LevelTracker还定义了两个类型方法与highestUnlockedLevel配合工作。第一个类型方法是unlockLevel,一旦新等级被解锁,它会更新highestUnlockedLevel的值。第二个类型方法是levelIsUnlocked,如果某个给定的等级已经被解锁,它将返回true。(注意,尽管我们没有使用类似LevelTracker.highestUnlockedLevel的写法,这个类型方法还是能够访问类型属性highestUnlockedLevel

除了类型属性和类型方法,LevelTracker还监测每个玩家的进度。它用实例属性currentLevel来监测玩家当前的等级。

为了便于管理currentLevel属性,LevelTracker定义了实例方法advanceToLevel。这个方法会在更新currentLevel之前检查所请求的新等级是否已经解锁。advanceToLevel方法返回布尔值以指示是否能够设置currentLevel

下面,Player类使用LevelTracker来监测和更新每个玩家的发展进度:

class Player {
    var tracker = LevelTracker()
    let playerName: String
    func completedLevel(level: Int) {
        LevelTracker.unlockLevel(level + 1)
        tracker.advanceToLevel(level + 1)
    }
    init(name: String) {
        playerName = name
    }
}

Player类创建一个新的LevelTracker实例来监测这个用户的进度。它提供了completedLevel方法,一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了advanceToLevel返回的布尔值,因为之前调用LevelTracker.unlockLevel时就知道了这个等级已经被解锁了)。

你还可以为一个新的玩家创建一个Player的实例,然后看这个玩家完成等级一时发生了什么:

var player = Player(name: "Argyrios")
player.completedLevel(1)
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
// 打印输出:highest unlocked level is now 2

如果你创建了第二个玩家,并尝试让他开始一个没有被任何玩家解锁的等级,那么试图设置玩家当前等级将会失败:

player = Player(name: "Beto")
if player.tracker.advanceToLevel(6) {
    print("player is now on level 6")
} else {
    print("level 6 has not yet been unlocked")
}
// 打印输出:level 6 has not yet been unlocked
时间: 2024-08-24 20:45:58

进击的雨燕---------------方法的相关文章

进击的雨燕--------------协议

详情转自:http://wiki.jikexueyuan.com/project/swift/chapter2/07_Closures.html 协议定义了一个蓝图,规定了用来实现某一特定工作或者功能所必需的方法和属性.类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能.任意能够满足协议要求的类型被称为遵循(conform)这个协议. 除了遵循协议的类型必须实现那些指定的规定以外,还可以对协议进行扩展,实现一些特殊的规定或者一些附加的功能,使得遵循的类型能够收益. 协议

进击的雨燕-------------扩展

详情转自:http://wiki.jikexueyuan.com/project/swift/chapter2/07_Closures.html 扩展就是向一个已有的类.结构体.枚举类型或者协议类型添加新功能(functionality).这包括在没有权限获取原始源代码的情况下扩展类型的能力(即逆向建模).扩展和 Objective-C 中的分类(categories)类似.(不过与 Objective-C 不同的是,Swift 的扩展没有名字.) Swift 中的扩展可以: 添加计算型属性和计

进击的雨燕-------------可空链式调用

详情转自:http://wiki.jikexueyuan.com/project/swift/chapter2/07_Closures.html 可空链式调用(Optional Chaining)是一种可以请求和调用属性.方法及下标的过程,它的可空性体现于请求或调用的目标当前可能为空(nil).如果可空的目标有值,那么调用就会成功:如果选择的目标为空(nil),那么这种调用将返回空(nil).多个连续的调用可以被链接在一起形成一个调用链,如果其中任何一个节点为空(nil)将导致整个链调用失败.

进击的雨燕-------------闭包

闭包是自包含的函数代码块,可以在代码中被传递和使用.Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似. 闭包可以捕获和存储其所在上下文中任意常量和变量的引用.这就是所谓的闭合并包裹着这些常量和变量,俗称闭包.Swift 会为您管理在捕获过程中涉及到的所有内存操作. 注意如果您不熟悉捕获(capturing)这个概念也不用担心,您可以在值捕获章节对其进行详细了解. 在函数章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采

进击的雨燕-------------枚举

枚举为一组相关的值定义了一个共同的类型,使你可以在你的代码中以类型安全的方式来使用这些值. 如果你熟悉 C 语言,你会知道在 C 语言中,枚举会为一组整型值分配相关联的名称.Swift 中的枚举更加灵活,不必给每一个枚举成员提供一个值.如果给枚举成员提供一个值(称为“原始”值),则该值的类型可以是字符串,字符,或是一个整型值或浮点数. 此外,枚举成员可以指定任意类型的关联值存储到枚举成员中,就像其他语言中的联合体(unions)和变体(variants).每一个枚举成员都可以有适当类型的关联值.

进击的雨燕--------------集合类型

Swift 语言提供Arrays.Sets和Dictionaries三种基本的集合类型用来存储集合数据.数组(Arrays)是有序数据的集.集合(Sets)是无序无重复数据的集.字典(Dictionaries)是无序的键值对的集. Swift 语言中的Arrays.Sets和Dictionaries中存储的数据值类型必须明确.这意味着我们不能把不正确的数据类型插入其中.同时这也说明我们完全可以对取回值的类型非常自信. 注意:Swift 的Arrays.Sets和Dictionaries类型被实现

进击的雨燕-------------类和结构体

类和结构体是人们构建代码所用的一种通用且灵活的构造体.我们可以使用完全相同的语法规则来为类和结构体定义属性(常量.变量)和添加方法,从而扩展类和结构体的功能. 与其他编程语言所不同的是,Swift 并不要求你为自定义类和结构去创建独立的接口和实现文件.你所要做的是在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其它代码的外部接口. 注意通常一个类的实例被称为对象.然而在 Swift 中,类和结构体的关系要比在其他语言中更加的密切,本章中所讨论的大部分功能都可以用在类和结构体上.因此,我

进击的雨燕---------------属性

属性将值跟特定的类.结构或枚举关联.存储属性存储常量或变量作为实例的一部分,而计算属性计算(不是存储)一个值.计算属性可以用于类.结构体和枚举,存储属性只能用于类和结构体. 存储属性和计算属性通常与特定类型的实例关联.但是,属性也可以直接作用于类型本身,这种属性称为类型属性. 另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作.属性观察器可以添加到自己定义的存储属性上,也可以添加到从父类继承的属性上. 存储属性 简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常

进击的雨燕----------继承

一个类可以继承(inherit)另一个类的方法(methods),属性(properties)和其它特性.当一个类继承其它类时,继承类叫子类(subclass),被继承类叫超类(或父类,superclass).在 Swift 中,继承是区分「类」与其它类型的一个基本特征. 在 Swift 中,类可以调用和访问超类的方法,属性和下标脚本(subscripts),并且可以重写(override)这些方法,属性和下标脚本来优化或修改它们的行为.Swift 会检查你的重写定义在超类中是否有匹配的定义,以