Swift学习笔记十二

方法

方法就是和某种特定类型相关联的函数。类、结构体、枚举都可以定义实例方法和类型方法。类型方法和OC中的类方法类似。

结构体和枚举也可以定义方法是Swift与C/OC之间很大的一个区别,在OC中,只有类才能定义方法。

实例方法

实例方法是从属于某个类实例或结构体实例、枚举实例的方法。他们提供与该实例相关的功能,比如获取更改属性或者提供其他函数功能。实例方法的语法和函数完全相同。

实例方法隐式地可以访问该类型的属性和方法。只能从该类型的实例上调用实例方法。比如:

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

let counter = Counter()
// the initial counter value is 0
counter.increment()
// the counter‘s value is now 1
counter.incrementBy(5)
// the counter‘s value is now 6
counter.reset()
// the counter‘s value is now 0

方法的内部和外部参数名称

如前所述,函数的参数可以同时拥有内部名称和外部名称,方法也是如此。不过,实例方法的内部和外部名称的默认行为和函数有所不同。

像OC一样,Swift中方法的名字一般都是用一个介词如with、for、by和第一个参数名称接应起来的。比如上个例子中的incrementBy(_:),介词使得方法在被调用的时候可读性更高,就像一个连贯的句子一样。因此Swift通过给方法参数提供了不同于函数的默认行为来使得这种方法命名方式更加便利:Swift给方法的第一个参数名提供了内部参数名,给第二个及以后的所有参数名同时提供了内部和外部参数名。

依旧以上一个Counter为例子,假设它的实例方法incrementBy需要两个参数:

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

默认情况下,Swift把第一个参数名amount只看做内部参数名,而把numberOfTimes同时看做内部和外部变量名。因此,在调用这个实例方法的时候:

let counter = Counter()
counter.incrementBy(5, numberOfTimes: 3)
// counter value is now 15

不需要给方法的第一个参数定义外部参数名,因为它的意义通过方法名incrementBy可以很明确地表现出来,但是后边的参数都需要外部参数名使得在方法调用的时候语义非常明确。这种默认的处理方式就像你在方法定义的时候已经在第二个及以后的每个参数名称前加上了(#)符号。

当然,如果你希望改变这种默认行为,给第一个参数指定外部参数名,也可以用通常的方法指定,如果你不想给第二个及以后的参数添加默认外部参数名,那么可以在参数名前加上下划线(_)作为外部参数名。

self属性

每个实例对象都有一个隐式属性self,它指向实例对象本身。在实际开发中,并不需要经常写self,在实例方法内部,如果你不显示地写明self,每次你在使用一直的属性或方法时,Swift都假定你是在用当前对象的属性或方法。这个原则的一个主要例外是当方法的参数名和实例对象的属性名相同时,方法的参数名会具有较高的优先级,如果需要使用实例对象的属性,就需要使用self。

从实例方法内改变值类型

结构体和枚举都是值类型的,默认情况下,值类型的属性是不能从它的实例方法内部改变的。

但是,如果你希望从一个特殊的方法里边改变结构体或枚举的属性值,你可以选择改变那个方法的行为。然后方法就可以从内部改变它的属性,并且它产生的任何改变在方法结束后都会写到原始的结构体上。方法还可以给它的隐式属性self赋一个全新的实例对象,当方法结束时,这个新实例将取代现有的那个。

通过在func前加上关键字mutating来实现上面的行为:

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)
println("The point is now at (\(somePoint.x), \(somePoint.y))")
// prints "The point is now at (3.0, 4.0)”

上面的示例中,定义了一个mutating方法moveByX,它的目的是将点对象移动一定距离,它是在当前的点对象上直接操作的,而不是返回一个新的点对象。

注意,如果你将值类型实例对象赋值给了一个常量,那你就不能调用它的mutating方法了,因为一旦赋值给常量,它的所有属性就都不能改变了,即使该属性是变量属性。

从Mutating方法内部给self赋值

Mutating方法可以给隐式属性self赋一个全新的对象,比如上面的例子可以改写成:

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)
    }
}

这个方法是返回了一个全新点对象实例,不过当方法完成时,结果和前面的例子是完全一样的。

枚举类型的Mutating方法可以将隐式属性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 is now equal to .High
ovenLight.next()
// ovenLight is now equal to .Off

类型方法

如前所述,实例方法就是在实例对象上调用的方法。而类型方法是指从类型本身调用的方法。通过在func前面加上关键字static来声明类型方法。类也可以用class关键字来允许子类可以重载父类对该方法的实现。

在OC中,只能在类上声明类型方法,而在Swift中,可以在类、结构体、枚举上声明类型方法。各种类型方法都显示地标明它支持的类型。

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

在类型方法内部,隐式属性self指向类型本身,而非它的实例对象。对结构体和枚举而言,这意味着当类型属性名和方法参数名相同的时候可以用来做区分。

通常情况下,在类型方法里边出现的未定义的方法和属性名字都会指向这个类型的类型属性和类型方法,不需要在属性和方法名字前加类型名称前缀。

下标

下标是获取集合、列表或者队列中元素的简便方法,下标可以通过索引序列(index)来获取和存储值,而不需要额外获取和设置方法。

类、结构和枚举可以定义下标,你可以为一个类型定义多个下标,下标也不局限于一维,可以定义具有多个参数的下标。

下标语法

下标语法通过在实例名称后面写上包含一个或多个值的中括号来使你可以查询实例对象。语法和实例方法语法及计算式属性语法都很像。通过subscript关键字来定义下标,然后像实例方法那样指定一个或多个输入参数及返回类型。和实例方法不同的是,下标可以是只读的活着是读写的。这个行为和计算式属性的getter和setter相似:

subscript(index: Int) -> Int {
    get {
        // return an appropriate subscript value here
    }
    set(newValue) {
        // perform a suitable setting action here
    }
}

这个setter和计算式属性类似的,如果你指定了新值参数名,则用那个名字,如果没有指定,默认提供的是newValue。

如果是只读下标,就去掉setter,和计算式属性类似,只有getter时可以省略get关键字用简写形式。

struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}
let threeTimesTable = TimesTable(multiplier: 3)
println("six times three is \(threeTimesTable[6])")
// prints "six times three is 18”

下标可以接受任意数目的任意类型的参数,也可以返回任意类型的返回值。下标可以使用变量参数以及可变参数,但是不能使用in-out参数,也不能给参数提供默认值。

类和结构体可以根据需要提供多种下表实现方式。至于哪种方式会被使用,取决于在使用下标的时候,方括号内参数类型,这也叫做下标重载。

下标通常都是接受一个参数,当然,也可以定义为接受多个参数:

struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(count: rows * columns, repeatedValue: 0.0)
    }
    func indexIsValidForRow(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    subscript(row: Int, column: Int) -> Double {
        get {
            assert(indexIsValidForRow(row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValidForRow(row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}

 var matrix = Matrix(rows: 2, columns: 2)

matrix[0, 1] = 1.5

 matrix[1, 0] = 3.2

继承

一个类可以从另一个类继承方法,属性或者其他特性,继承的类叫子类,被继承的类叫超类。继承是类区别于其他类型的一个主要特征。

类可以访问它超类的属性、方法和下标,也可以提供自己对这些属性、方法和下标的重载版本来改变它们的行为。类也可以给继承的属性添加属性观察者,无论这个属性在超类中是存储式属性还是计算式属性。

任何不继承自其他类的类被称为基类。Swift中的类并没有统一继承自某个全局基类,如果你创建的类没有继承自任何类,它就是基类。

class Vehicle {
    var currentSpeed = 0.0
    var description: String { //这是一个只读的计算式属性
        return "traveling at \(currentSpeed) miles per hour"
    }
    func makeNoise() {
        // do nothing - an arbitrary vehicle doesn‘t necessarily make a noise
    }
}

这里定义了一个没有继承自任何类的基类,它的一个实例方法并没有实现,这是留给子类去具体实现的。

子类的定义是在类名后边加上冒号,然后加上超类的名称:

class Bicycle: Vehicle {
    var hasBasket = false
}

子类默认就继承了超类的特性,比如Bicycle类就自动有currentSpeed属性及makeNoise方法等。当然,子类还可以定义自己的属性和方法,子类也可以被再次继承,子类会依次获得继承链上那些超类的特性。

重载

子类可以给继承来的实例方法、类型方法、实例属性、类型属性及下标提供自己的实现版本,否则它们的实现方式就是继承自超类的。这叫做重载。

在重载实现前面加上关键字override来标明重载某个特性,这表示你是有意要重载某个特性,而不是不小心用了同样的名称或定义。事实上,任何不带override关键字的重载都会在编译时触发错误。override关键字也会让Swift检查重载是否和超类中的特性匹配,以确保重载的定义是正确的。

有时候,在子类重载超类特性的时候,可能需要访问超类对该类的实现,这通过super前缀来访问超类的属性、方法及下标实现。

重载方法就是在方法的func前加上override关键字.

任何继承来的属性都可以被重载,你可以提供自定义的getter,需要的时候也可以提供自定义的setter,无论这个属性在超类中是定义为存储属性或是计算属性。超类中属性究竟是存储式还是计算式子类是无法知道的,它只知道这个属性的名字和类型。你必须同时写明重载的属性的名称和类型以便Swift检查重载是否正确。你可以通过提供getter和setter将一个只读的属性在子类中重载为读写的属性,然后,你不能将一个可读写的属性重载为只读的。

注意:如果你重载的时候提供了setter,那么你必须同时提供getter作为重载的一部分。

class Car: Vehicle {
    var gear = 1
    override var description: String {
        return super.description + " in gear \(gear)"
    }
}

你可以通过属性重载来给继承来的属性添加观察者,无论这个属性最初是如何实现和定义的,在它发生改变的时候你都可以收到通知。

注意:你不能给继承来的常量存储属性或者只读的计算属性添加观察者,这些属性是不能被改变的,因此在重载时提供willSet和didSet是不合适的。并且,你也不能同时重载一个属性的setter和它的观察者,因为如果你重载了setter,你可以直接在setter里边实现观察者的目的。

class AutomaticCar: Car {
    override var currentSpeed: Double {
        didSet {
            gear = Int(currentSpeed / 10.0) + 1
        }
    }
}

let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
println("AutomaticCar: \(automatic.description)")
// AutomaticCar: traveling at 35.0 miles per hour in gear 4

阻止重载

你可以通过标明最终版本来阻止方法、属性或下标被重载,在他们关键字前加上final关键字即可,比如:final var, final func, final class func, final subscript等。

任何试图重载已经标记为final的特性都会触发编译时错误。

你也可以在类关键字class前加上final关键字来将整个类标记为最终版本,任何试图继承final类都会触发编译时错误。

时间: 2024-10-07 04:24:44

Swift学习笔记十二的相关文章

Swift学习笔记十二:下标脚本(subscript)

下标脚本就是对一个东西通过索引,快速取值的一种语法,例如数组的a[0].这就是一个下标脚本.通过索引0来快速取值.在Swift中,我们可以对类(Class).结构体(structure)和枚举(enumeration)中自己定义下标脚本的语法 一.常规定义 class Student{ var scores:Int[] = Array(count:5,repeatedValue:0) subscript(index:Int) -> Int{ get{ return scores[index];

Swift学习笔记十四:构造(Initialization)

类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值.存储型属性的值不能处于一个未知的状态. 你可以在构造器中为存储型属性赋初值,也可以在定义属性时为其设置默认值.以下章节将详细介绍这两种方法. 注意: 当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观测器(property observers). 一.基本语法 class Human{ var name :String init(){ name = "human" } init(n

Swift学习笔记十:属性

1.存储属性       1. 作为特定类或结构实例的一部分,存储属性存储着常量或者变量的值.存储属性可分为变量存储属性(关键字var描述)和常量存储属性(关键字let描述). struct student{ let name = "" var score = 0 } let a = student(name:"小笨狼",score:96)           注意:                ① 定义储存属性时,需要为每一个属性定义一个默认值.在初始化的时候,

虚拟机VMWare学习笔记十二 - 将物理机抓取成虚拟机

1. 安装VMware vCenter Converter Standalone Client 运行虚拟机,File -- Virtualize a Physical Machine 这时如果电脑中没有VMware vCenter Converter Standalone Client ,则会进行安装. 安装过程 之后图标会出现在桌面上,双击运行 选择连接到本地服务器,登陆 点击转换计算机 这个,可以将本地计算机抓取成虚拟机,也可以将其他可以访问的计算机(需知道管理员用户名及密码)抓取成虚拟机.

Swift学习笔记(二):属性、元组

一.属性的getter和setter //设置计算型属性:其并不真正的存储值,而是每次通过其他值计算得来 var subtotal: Double { //getter:通过total.taxPct计算获得total的值 get { return total / (taxPct + 1) } //setter:更新的是相关的值(比如此处基于newSubtotal来设置total.taxPct的值) set(newSubtotal) { //... } } 二.元组 | Tuples //创建一个

Swift 学习笔记十五:扩展

扩展就是向一个已有的类.结构体或枚举类型添加新功能(functionality).扩展和 Objective-C 中的分类(categories)类似.(不过与Objective-C不同的是,Swift 的扩展没有名字.) Swift 中的扩展可以: 1.添加计算型属性和计算静态属性 2.定义实例方法和类型方法 3.提供新的构造器 4.定义下标 5.定义和使用新的嵌套类型 6.使一个已有类型符合某个协议 一.扩展属性,构造器,方法 class Human{ var name:String? va

swift学习笔记之二——集合

//=========================== //2014/7/21 17:27 swift集合 //=========================== swift提供了两种集合类型,arrays和dictionaryies,两种集合都是可变的,可以在集合声明后对其进行新增.删除和修改操作. 1.array 数组的定义与java数组相同,但swift的数组提供了更灵活的创建方式和操作方式. 数组创建和初始化方式: var array1: Array<T> = [val1,va

《Hibernate学习笔记十二》学生、课程、分数关系的设计与实现

<Hibernate学习笔记十二>学生.课程.分数关系的设计与实现 这个马士兵老师的Hibernate视频学习的一个题目,这里面要用到多对多.多对一的关联关系以及联合主键,因此觉得挺好的,自己写篇博文来记录下. 先考虑数据库表 1.学生表:为简单起见,只考虑了学生id和学生姓名,其中id为主键 2.课程表:为简单起见,只考虑了课程id和课程名称,其中id为主键 3.分数表 分数表有两种解决方案 3.1 第一种为:使用联合主键:student_id 和 course_id 3.2 第二种:不使用

laravel3学习笔记(十二)

原作者博客:ieqi.net ==================================================================================================== 请求反射 HTTP 协议本身是无状态性的,但是在应用中处理各种业务逻辑时我们必须要有状态的把控,这样,折中的办法就是将状态进行标记然后嵌入到 HTTP 协议的请求中,然后应用根据这些标记来进行状态的串联以及处理.所以我们就要对请求进行反射处理以获取请求信息, Lara