方法—Swift学习笔记(十四)

注:本文为自己学习The Swift Programming Language的笔记,其中的例子为引用原书和其他博文或自己原创的。每个例子都会批注一些实践过程中的经验或思考总结。

1.基础

方法就是和类型相关的函数,类、结构体和枚举都可以定义实例方法和类方法。与实例属性和类属性的区别一样,实例方法是属于实例的一个完成特定功能的函数,而类方法是属于类的。结构体和枚举能定义方法是Swift和其他类C语言最大的不同点之一。

2.实例方法

实例方法是属于特定类、结构体和枚举的实例的函数,可以实现获取或修改实例属性、提供与类的目的相关的功能等。

在类型定义内部,实例方法可以隐式的被该类型的其他的实例方法或实例属性所调用;而在类型定义外部,实例方法只能被某一个该类型的实例调用,而不能在没有实例的情况下直接调用。

实例方法的定义如同普通的函数,func关键字打头。下面定义一个计数类counter:

class Counter {
    var count = 0
    func increment() {
        count++
    }
    func incrementBy(amount: Int) {
        count += amount
    }
    func reset() {
        count = 0
    }
}
它有两个函数increment和incrementBy,实例方法的定义可以参见函数定义。调用实例方法,先要创建一个类的实例:

let counter = Counter()
counter.incrementBy(100)
counter.reset()
2.1实例方法的局部和外部参数名

和函数一一样,方法的参数也可以有局部参数名[local parameter]和外部参数名[external parameter],但是他们的默认行为和函数不同。

为了使方法在被调用时的代码读起来就像一句话一样,Swift方法命名规范沿用的Objective-C中加介词的方法,其中包括了with、for和by等。与Objective-C把介词放在第一个参数外部参数名内不同的是,Swift采用不同的默认行为来解决这个问题。Swift默认第一个参数只有局部变量名,而后面其他的参数都有默认的外部和局部参数名。

对Counter类中的incrementBy方法重载:

class Counter {
    var count = 0
    func increment() {
        count++
    }
    func incrementBy(amount: Int) {
        count += amount
    }
    func incrementBy(amount: Int, numberOfTimes : Int) {
        count += amount * numberOfTimes
    }
    func reset() {
        count = 0
    }
}
那么在调用时,第一个参数不带外部参数名,第二个参数带上默认和内部参数名一样的外部参数名,这样读起来的确像一句通顺的话了:

counter.incrementBy(5, numberOfTimes : 3)
默认的第二个参数外部参数名让方法在调用时目的性更加明确。

我们也可以添加#号符号显式的区别这一个默认行为,我们也可以显式的修改外部参数名,也能用下划线 _ 符号显式的表示第二个及以后的参数不带外部参数名。

2.2self属性

每一个类型的实例都有一个叫做self的隐式属性,它等价于这个实例本身。self属性配合点符号使用。

由于编译器可以判断指代,一般情况下不用显式的给出self + . 符号 + 属性/方法,而是直接调用属性/方法。除非,这个实例方法的参数名和这个实例的某些属性名相同时,self才排上用场(编译器不能判断指代时):

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) {
    println("This point is to the right of the line where x == 1.0")
}
这里实例方法参数名x和实例属性名x一样,用self加以区别。其实self较为常用的时候是在init构造函数中:

    init(x : Double, y : Double) {
        (self.x, self.y) = (x,y)
    }
 不过这个构造函数时默认的构造函数。

2.3实例方法修改值类型

值类型的实例方法不能直接修改属于值类型的属性,Swift使用mutating关键字实现。

在实例函数func之前添加mutating关键字,表明这个函数可以修改值类型,给Point结构体添加moveByX方法:

    mutating func moveByX(deltaX : Double, deltaY : Double) {
        x += deltaX
        y += deltaY
    }
注意常量实例不能调用mutating methods。除了这样分别的修改属性,也可以利用隐式属性self直接赋值一个全新的实例,比如上面的方法可以等价写成:

    mutating func moveByX2(deltaX : Double, deltaY : Double) {
        self = Point(x : x + deltaX, y : y + deltaY)
    }

3.类型方法

类型方法是不需要实例就可以调用的方法,类的类型方法用class关键字+func定义,结构体&枚举的类型方法用static关键字+func定义, 调用是类型名后面加点+类型方法格式。

类型方法中的隐式属性self指的是类型本身,而不是某个实例。

一个类型方法可以在内部调用其他的类型方法,不用添加self前缀或者类型名称和点符号前缀;也可以修改和访问类型属性。实例方法当然也可以调用类型方法,但要给出类型名和点符号前缀的完整形式。

下面定义一个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
        }
    }
}
之所以这么定义这个结构体,是因为当前关数和当前过关是某一个实例的记录和行为,而解锁关数是属于这个游戏本身的,而不是某个游戏的实例。

游戏设定为支持多用户的单人游戏,因而定义玩家Player类,它有一个过关记录tracker:

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
    }
}
玩家在完成一个关卡后调用completeLevel方法,先更新最大解锁关数,在进行进入下一关动作。

创建一个Player实例,当他完成关卡1后立即解锁关卡2:

var player1 = Player(name: "JCGuo")
player.completedLevel(1)
println("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
// prints "highest unlocked level is now 2
这时候如果创建另一个玩家实例,他想玩第6关,就会返回第6关被锁住的信息:

var player2 = Player(name: "John Snow")
if player2.tracker.advanceToLevel(6) {
    println("player2 is now on level 6")
} else {
    println("level 6 has not yet been unlocked")
}
// prints "level 6 has not yet been unlocked


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值