在class后面跟类名来创建一个类。除了上下文是类以外,声明一个属性和常量,变量声明是一样的,方法和函数也是如此。
试一试:
- class Shape {
- var numberOfSides = 0
- func simpleDescription() -> String {
- return "A shape with \(numberOfSides) sides."
- }
- }
通过在类名的后面加小括号来创建这个类的实例。用点语法来访问这个实例的属性和方法。
- var shape = Shape()
- shape.numberOfSides = 7
- var shapeDescription = shape.simpleDescription()
上个版本的Shape类缺失了一个重要的东西:通过初始化器来创建一个实例。用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是属性还是参数。创建一个类的实例时这里的参数传递就像函数调用时的参数传递一样。每一个属性都需要赋值,在声明的时候(如numberOfSides)或是在初始化器中(如name)。
当你需要在对象释放前做一些清理工作,可以用deinit来创建一个析构器。
子类类名后面是父类名,中间用冒号: 隔开。没有强求子类要有标准的根类,所以可以根据需要省略父类。
子类的方法要覆盖父类方法的实现时,要用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()
试一试:
写一个继承自NameShape的子类Circle,初始化器中接收radius和name参数。并实现area和describe方法。
除了简单的存储属性,属性还有getter和setter方法。
- 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 triagle with sides of length \(sideLength)."
- }
- }
- var triangle = EquilateralTriangle(sideLength: 3.1,name: "a triangle")
- triangle.perimeter
- triangle.perimeter = 9.9
- triangle.sideLength
在perimeter的setter方法中,新值有一个隐式的名newValue.你可以在set方法的后面加一个括号显式的提供一个新值名字。
对于EquilateralTriangle中的初始化器做了三件事:
1.设置子类声明的属性的值
3.改变被父类定义的属性值。当然通过其他的如方法,getter,setter,都可以完成这一点。
如果你不需要计算属性但是又需要在设置一个新值之后,在运行之前提供一些代码,可以用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")
- triangleAndSquare.square.sideLength
- triangleAndSquare.triangle.sideLength
- triangleAndSquare.square = Square(sideLength: 50,name: "larger square")
- 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)
当用到optional值时,你可以在操作之前加一个问号?。如果在?之前的值是nil,那么问号?之后的代码就被忽略,都被视为是nil。否则,这个optional值被解包,问号?之后的就作为解包值。下面两种情况下,整个表达式的值都是一个optional值。
- let optionalSquare: Square? = Square(sideLength: 2.5,name: "optional square")
- let sideLength = optionalSquare?.sideLength