对象和类

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,初始化器中接收radiusname参数。并实现areadescribe方法。


除了简单的存储属性,属性还有gettersetter方法。

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.设置子类声明的属性的值

2.调用父类的初始化器

3.改变被父类定义的属性值。当然通过其他的如方法,getter,setter,都可以完成这一点。


如果你不需要计算属性但是又需要在设置一个新值之后,在运行之前提供一些代码,可以用willsetdidset。比如,下面的类确保了三角形和正方形的边长都相同。

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