Swift是苹果公司开发的一门新语言,它当然具备面向对象的许多特性,现在开始介绍Swift中类和对象的语法。
对象和类
用"class"加上类名字来创建一个类,属性声明和声明常量或者变量是一样的,只是它是在类里边声明的而已。方法和函数声明也是一样的:
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
创建类对象也很简单,注意这里没有用new关键字,类对象的属性和方法的访问方式是通过.实现的
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
类的声明里边,通常都会有初始化方法init,
class NamedShape {
var numberOfSides: Int = 0
var name: String init(name: String) {
self.name = name
} func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
注意这里用self来指代当前类对象,这样就区分了属性name和传入init方法的参数name。
每个属性都需要被赋值,要么在声明它的时候,或者在初始化方法中。
用deinit方法来创建解构函数,在类对象被释放之前做一些清理工作。
类的继承语法也很简单,用冒号加上父类名称就可以了。没有约定子类必须要继承自哪些根类,因此继承不是必须的。子类通过override标识覆盖父类方法。如果子类中有与父类同名的方法而没有override,则编译器会报错。编译器也会探测标识了override但是父类中又没有同名的方法。
class Square: NamedShape {
var sideLength: Double init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
} func area() -> Double {
return sideLength * sideLength
} override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
简单属性可以直接保存,属性也可以有存取器:
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0 init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
} var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
} override func simpleDescription() -> String {
return "An equilateral triangle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
println(triangle.perimeter)
triangle.perimeter = 9.9
println(triangle.sideLength)
在setter中,新值的名称隐式地是newValue,也可以在set后显示地指明别的名称。
如果你的属性不需要计算,但是在设定值之前或者之后仍然需要写一些逻辑,可以用willSet和didSet,比如,下边的类就确保它的三角形的边长和正方形的边长始终相等:
class TriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
square.sideLength = newValue.sideLength
}
}
var square: Square {
willSet {
triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = EquilateralTriangle(sideLength: size, name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
println(triangleAndSquare.square.sideLength)
println(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
println(triangleAndSquare.triangle.sideLength)
类的方法和一般函数有一个很重要的区别。一般函数的参数名只在函数内部被用到,但是类方法的参数名在你调用方法的时候也被用到了(第一个参数除外)。默认情况下,当你调用某个方法的时候,该方法与其内部都有相同的参数名。你可以指定另外的名称,这个名称将在方法内部使用:
class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes times: Int) {
count += amount * times
}
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)
当处理可省略变量的时候,可以在运算比如属性、方法、下标操作之前加上问号。如果问号之前的值是nil,问号之后的所有表达式被忽略掉,整个表达式的值也是nil。否则,可省略值被展开,问号之后的所有表达式都基于展开的可省略值。无论如何,整个表达式的值都是可省略值。
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
枚举类型和结构体
用enum来创建枚举类型变量。像类和其他类型一样,枚举类型也可以有与之相应的方法:
enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescription() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "queen"
case .King:
return "king"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.Ace //Enum Value
let aceRawValue = ace.rawValue //1
在上边的例子里,枚举类型变量Rank的原始类型(raw-type)是Int类型,因此只需要给第一个值赋值,剩下的值会一次自动赋予。也可以把枚举变量的原始类型设定为字符串或者浮点数,用rawValue来获取枚举变量的原始值。用init?(rawValue:)初始化器从一个原始值创建一个枚举类型的实例。
if let convertedRank = Rank(rawValue: ) {
let threeDescription = convertedRank.simpleDescription()
}
枚举类型的成员值就是实际的值,并不是原始值(raw value)的另一种书写方式,事实上,并没有所谓的真正意义上的原始值,并不需要为每个枚举类型成员赋原始值
enum Suit {
case Spades, Hearts, Diamonds, Clubs
func simpleDescription() -> String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
}
}
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription()
注意这里用了两种引用枚举类型实例的成员值的方式:当把它作为值复制给常量hearts时,是用的显示地指明全称Suit.Hearts,因为常量没有隐式地指明类型;而在switch内部,枚举类型的成员值是用简写方式.Hearts被引用的,这是因为self的类型已经是已知合适的类型了。在任何变量类型已知并且合适的时候,都可以使用这种简写方式。
结构体
用struct关键字来创建结构体,结构体支持很多类的行为,包括方法和初始化器。结构体和类最大的区别在于当在代码里传递的时候,结构体永远是被拷贝的,而类只传递了引用。
struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())" //在字符串中直接引用表达式或者变量
}
}
let threeOfSpades = Card(rank: .Three, suit: .Spades) //注意这种简写形式,是因为rank和suit的变量类型都已经确定了
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
枚举类型成员的实例可以拥有和实例关联的值(associated value),枚举类型成员的不同实例可以有不同的值和它们相关联,这些关联值是在创建实例的时候提供的。
关联值(associated value)和原始值(raw value)是不同的:枚举类型成员的原始值对所有实例而言是相同的,在定义该枚举类型的时候就需要给出原始值。
举个栗子,在向一个服务器请求日出和日落时间的数据,服务器的返回分为两种情况:返回相应的时间或者返回一个错误
enum ServerResponse {
case Result(String, String)
case Error(String)
} let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.") switch success {
case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
let serverResponse = "Failure... \(error)"
}
注意sunrise和sunset是如何作为匹配switch的case的一部分从ServerResponse中被取出的。