首页 > 代码库 > swift 笔记 (十一) —— 方法(类,结构体,枚举)

swift 笔记 (十一) —— 方法(类,结构体,枚举)

Methods (方法)

实例方法(Instance Methods)
我觉得看到这里,我们唯能八一八的就是swift的自做主张的行为了,反正它就是会以各种方式帮助我们来完成让代码看起来很奇怪的事情。。。

在之前函数那一篇笔记中,我们已经看到过参数的外部名字和内部名字的区分了,当然,在实例方法中,这个事情依然存在,而且,swift又会帮我们做一些潜规则。

swift在默认的情况下,会为方法的第一个参数只提供函数内部使用的名字,而从第二个参数开始,既可以外部用,也可以内部用(就像我们在函数中见过的  External Parameter 一样):

class MyClass{
     var myNum: Int = 0
     func increaseNum(base: Int, numberOfTimes: Int) {
          myNum += base * numberOfTimes
     }
}

var myClassInstance = MyClass()
myClassInstance.increase(10, numberOfTime: 8)   //第二个参数开始,会自动的提供一个外部使用变量名 
(感谢 swift技术交流第一平台(355277)的群友 偏未晚(1027115179) 指出错这里的函数名笔误 )
myClassInstance.increaseNum(10, numberOfTime: 8)   //第二个参数开始,会自动的提供一个外部使用变量名

这就相当于,我们在函数那部分提到的函数想为外部提供一个变量名的话,有两种做法:
1.  func myFunc(value paramA: Int)  {……}    //value是给外部用的名字, paramA是给内部用的
2.  func myFunc(#paramA: Int) {……}     //paramA 既是外部用的,也是内部用的

而在上面的MyClass类的实例方法中,相当于,从第二个参数开始,swift自动为我们提供人了”#”的功能,而不需要我们明确的写#
这个默认的行为,有时候是非常讨厌的,所以swift依然提供给我们不给外部提供名字的方法:
class MyClass{
     var myNum: Int = 0
     func increaseNum(base: Int, numberOfTimes: Int) {      //注意这一行有一个 “_"
          myNum += base * numberOfTimes
     }

在参数的名字前面加了一个”_”和一个空格,这样,swift便不再会自动提供外部使用名字了。
当然,如果我们把”_”这里,换成别的名字,那么就跟之前说的函数是一样的了。

self 属性
每个实例,都有一个属性用于表示它自己这个实例,这个属性叫做self。
class MyClass{
     var  num: Int = 0
     func setNum(num:Int) {
          self.num = num            //注意这里的self
     }
如果出现这个setNum方法的情况,传入的参数名叫num, 而我们设置的属性也叫num的时候,为了区分,到底是传入的值还是实例的属性,我们为实例属性前面加了self,表示是这个实例自己的属性。

Mutating方法
以上面说的都是类的情况,那么对于 struct, enum这样的值传递类型,却不能使用实例方法来改变属性的值。
struct MyPoint {
     var x = 0, y = 0
     mutating func moveByXY(deltaX: Int, y deltaY: Int) {    //注意这里的mutating关键字
          x += deltaX
          y += deltaY
     }
}

var instance = MyPoint()
instance.moveByXY(3, y: 4)
println(“x:\(instance.x)  y:\(instance.y)”)
在struct, enum里使用mutating关键字,以达到和class里一样的效果
注意:如果上面的x,和y 不是var,而是let的话,是不能用的,因为我们不能改变一个常量的值。

在Mutating方法中,给self赋值
struct MyPoint {
     var x = 0, y = 0
     mutating func changeSelf(deltaX: Int, y deltaY: Int) {
          self = MyPoint (x: deltaX, y: deltaY)      //注意这里的self关键字
     }

var instance = MyPoint()
instance.changeSelf(3, y: 4)
println(“x:\(instance.x)  y:\(instance.y)”)

这种方式,也同样达到了改变 instance中的x和y的目的,然而,这种给self赋值的方法,却是生成了一个新的MyPoint的实例(而之前的实例是如何销毁的,内存如何释放的,要等到后面讲到内存管理的章节才能说到,这里暂时不考虑)

enum MyStateMachine {
     case None, Init, Run, Deinit
     mutating func nextState() {
          switch self {
               case None:
                    self = Init
               case Init:
                    self = Run
               case Run:
                    self = Deinit
               case Deinit:
                    self = None
          }
     }
}

var stateMachine = MyStateMachine.None
stateMachine.nextState()                                        //self变成 Init
println(stateMachine == MyStateMachine.Init)          // true

stateMachine.nextState()                                        //self 变成 Run
println(stateMachine == MyStateMachine.Init)          //true

这就很有意思了,状态机的状态切换的代码可以直接写在enum的定义中

类型方法
上面提到的方法,都是实例相关的方法,它们都是针对某一个实例进行操作的,而类型方法,是对于这个类型的,其实这种方法在上一篇笔记中,类型属性的那部分已经出现过关键字了:static和class。

class MyClass {
     class var computedProperty: Int {          //这是一个类型属性,上一篇笔记中提到的

     }

     class func someMethod() {                    //这才是一个类型方法

     }
}

因为类里面的类型属性只能用于计算,不能保存数据,所以类里的类型方法,暂时还不适合写一个可以跑起来的例子,应该结合struct 或者enum,才可以写,还是先看看struct的类型方法吧,enum和struct差不多(这是个官方例子, 方便复制就不帖图了,仅仅是抄了一遍,因为我非常喜欢这个例子,在实际的游戏开发中很常用):

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 {               //Mutating方法,是个实例方法
          if LevelTracker.levelIsUnlocked(level) {
               currentLevel = level
               return true
          } else {
               return false
          }
     }
}

class Player {
     var tracker = LevelTracker()          //上面定义的结构体的一个实例
     let playerName: String
     func completedLevel( level: Int) {
          LevelTracker.unlockLevel(level + 1)          //调用了上面那个结构体的类型方法
          tracker.advanceToLevel(level + 1)             //调用了tracker这个实例的Mutating方法(实例方法)
     }

     init(name: String) {
          playerName = name                  //关于这个playerName常量初始化问题,在后面的章节会提到
     }
}

var player = Player(name: “World”)
player.completedLevel(1)                    //这个函数调用后 tracker的highestUnlockedLevel 会变成2

println(“highest unlocked  level is now \(LevelTracker.highestUnlockedLevel)”)

var player2 = Player(name: “Kitty”)
if player2.tracker.advanceToLevel(6) {          //检测是否可以把player2设置为6级
     println(“player is now on level6")
} else {
     println(“level 6 has not yet been unlocked")
}

运行结果是else中的println被执行。
这个例子,如果上面的内容都看懂了的话,读起来应该很容易,如果还是不懂,那么请回结合上一篇笔记一起看。