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: 3) { 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中被取出的。