Swift学习3---类和对象

在Swift中,类的初始化要经过两个阶段

(1)第一个阶段,每一个存储属性都被设置了一个初始值。

(2)在第二个阶段,每个类在这个实例被使用之前都会有机会来设置它们相应的存储属性

1.创建和使用类

  使用 class 和类名来创建一个类。类中属性的声明和常量、变量声明一样,唯一的区别就是 它们的上下文是类。同样,方法和函数声明也一样。 

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with (numberOfSides) sides."
    }
}

  创建Shape类的实例,并调用其字段和方法。

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."
    }
}

注意:通过init构建对象,既可以使用self显式引用成员字段(name),也可以隐式引用(numberOfSides)。

   如果你需要在删除对象之前进行一些清理工作,使用 deinit 创建一个析构函数进行清理。 

2.继承和多态

  子类的定义方法是在它们的类名后面加上父类的名字,用冒号分割。创建类的时候并不需要 一个标准的根类,所以你可以忽略父类。 

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

  为了简化代码,Swift引入了属性(property),见下面的perimeter字段:

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

注意:赋值器(setter)中,接收的值被自动命名为newValue你可以在 set 之后显示的设置一个名 字。 

3.willSet和didSet

  注意 EquilateralTriangle 类的构造器执行了三步:
  1. 设置子类声明的属性值
  2. 调用父类的构造器
  3. 改变父类定义的属性值。其他的工作比如调用方法、getters 和 setters 也可以在这个阶 段完成。

  如果你不需要计算属性但是需要在设置一个新值之前运行一些代码,使用 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.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLengt

比如,上面的类确保三角形的边长总是和正方形的边长相同 

4.调用方法

  Swift中,函数的参数名称只能在函数内部使用,但方法的参数名称除了在内部使用外还可以在外部使用(第一个参数除外),例如:

class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, numberOfTimes times: Int) {
        count += amount * times
    }
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)
//注意Swift支持为方法参数取别名:在上面的代码里,numberOfTimes面向外部,times面向内部。

5.?的另一种用途

  使用可空值时,?可以出现在方法、属性或下标前面。如果?前的值为nil,那么?后面的表达式会被忽略,而原表达式直接返回nil,例如:

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional 
square")
let sideLength = optionalSquare?.sideLength
//optionalSquarenil时,sideLength属性调用会被忽略。

6.补充

初始化

  init关键词,和OC一样。不过这块非常像CPP,如下面这东西

class nyan{ 
        var num: Int = 1024 
        init() 
        { 
            num = 1; 
        } 
    } 
      
    class miao:nyan 
    { 
        var num1: Int = 1

      override init() { 

            num1 = 2 
        } 
    } 

var a = miao() 
      
println(a.num) 
println(a.num1) 
//
如果以OC的思维来看,它输出的应该是 1024, 2。因为OC只要没有call super,就是覆盖掉了。
//但实际上Swift输出的是 1, 2。倘若,我们在两个init加上打log的话,会发现,子类的init先运行,然后运行父类的。那么什么时候在子类的init里面call super.init()呢?
显而易见:当在子类里面要调用父类的成员变量时,要先调用super.init()去初始化它,若没有写的话,XCode会报编译错误。
原文地址:https://www.cnblogs.com/ShawnLi/p/4502116.html