Swift之类和结构体

在Swift中,类和结构体既相似又各有不同。

基本知识点:基本语法、类型方法、mutating方法、类和结构体异同点、类和结构体的选择

基本语法

  1. 结构体和类都可以添加属性和方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 结构体
    struct Resolution {
    var width = 0
    var height = 0
    func PrintDesc(){
    print("this is a structure instance method")
    }
    }

    // 类
    class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
    func PrintVideoDesc(){
    print("this is a VideoMode instaince method")
    }
    }
  2. 点语法访问或赋值

    1
    2
    let someResolution = Resolution()
    let someVideoMode = VideoMode()
  3. Swift允许直接设置结构体属性的子属性

    1
    2
    3
    let width = someResolution.width
    let width2 = someVideoMode.resolution.width
    someVideoMode.resolution.width = 1280
  4. 结构体都有一个自动生成的成员逐一构造器,用于初始化结构体成员的属性

    1
    let vga = Resolution(width: 640, height: 1280)
  5. 结构体是值类型,类是引用类型。

    值类型被赋予给一个变量、常量或者被传递给一个函数的时候,其值会被拷贝。Swift中,所有的基本类型如整型、浮点数、布尔值、字符串、数组和字典都是值类型,并且在底层都是以结构体的形式实现的。在Swift中,所有的结构体和枚举类型都是值类型。所以它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制。

    引用类型在被赋予到一个变量、常量或者被传递到一个函数时,其值不会被拷贝。引用的是已存在的实例本身而不是其拷贝。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //声明一个叫tenEighty的常量
    let tenEighty = VideoMode()
    tenEighty.resolution = hd
    tenEighty.interlaced = true
    tenEighty.name = "1080i"
    tenEighty.frameRate = 25.0

    //把tenEighty赋给新常量alsoTenEighty,并修改alsoTenEighty
    let alsoTenEighty = tenEighty
    alsoTenEighty.frameRate = 30.0

    print("the frameRate property of tenEighty is now \(tenEighty.frameRate)")
    //the frameRate property of tenEighty is now 30.0

    注意:

    tenEightyalsoTenEighty被声明为常量而不是变量。然而你依然可以修改tenEighty.frameRate alsoTenEighty.frameRate。因为tenEightyalsoTenEighty这两个常量的值并未改变。他们并不存储这个VideoMode实例,而仅仅是对VideoMode实例的引用。所以改变的是被引用的VideoModeframeRate属性,而不是引用VideoMode的常量。

  6. 类的继承

    类不同于结构体和枚举的重要一点就是继承。继承可以重写类的实例方法和类型方法。被override的方法前需要加上override关键字来标记重写。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class A{
    var name: String?
    var voice: String?
    func printDesc(){
    print"this is A instance method."
    }
    }

    class AA: A{
    var language: String = "English"
    // 重写父类方法
    override func printDesc(){
    print("this is a new instance method")
    super.printDesc()//调用父类方法
    }
    }
  7. 恒等运算符

    因为类是引用类型,有可能有多个常量和变量同时引用同一个类实例。如果能够判定两个常量或者变量是否引用同一个类实例会很有帮助。运用等价于===和不等价于!==这两个运算符可以检测两个常量或者变量是否引用同一个实例。

    1
    2
    3
    4
    if tenEighty === alsoTenEighty {
    print("tenEighty and alsoTenEighty refer to the same Resolution instance.")
    }
    //打印 "tenEighty and alsoTenEighty refer to the same Resolution instance."

    ===不同于==

    等价于===表示两个类类型的常量或者变量引用同一个类实例。等于==表示两个实例的值相等或相同。

类型方法

  1. 结构体的类型方法用static关键字,类的类型方法用class关键字

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 结构体
    struct Square{
    static func numberOfSides() -> Int{
    return 4
    }
    }
    let sides = Square.numberOfSides()

    //类
    class Rectangle{
    class func numberOfSides() -> Int{
    return 4
    }
    }

    let rectangleSides = Rectangle.numberOfSides()
  2. 禁止子类自己实现的类型方法用static关键字或final class

    1
    2
    3
    4
    5
    6
    7
     static func funcName(){
    //子类不能通过override class func funcName来重写此方法
    }

    final class func funcNameAgain(){
    //子类不能重写
    }

    关键字static告诉编译器不要让子类为这个方法提供自己的实现。

mutating方法

结构体中,一个实例方法要修改结构体的属性,必须用mutating标记。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct Person{
var firstName = "Matt"
var lastName = "Mathias"

mutating func changeTo(firstName: String, lastName: String){
self.firstName = firstName
self.lastName = lastName
}
}

var p = Person()
p.changeTo(firstName:"Mary", lastName:"Gallagher")
print(p.firstName)

类和结构体比较

共同点:

  1. 定义属性用于存储值
  2. 定义方法用于提供功能
  3. 定义下标操作使得可以通过下标语法来访问实例所包含的值
  4. 定义构造器用于生成初始值
  5. 通过扩展以增加默认实现的功能
  6. 实现协议以提供某种标准功能

与结构体相比,类的附加功能:

  1. 继承允许一个类继承另一个类的特征
  2. 类型转换允许在运行时检查和解释一个类实例的类型
  3. 析构器允许一个类实例释放任何其所被分配的资源
  4. 引用计数允许对一个类的多次引用

注意:

结构体总是通过被复制的方式在代码中传递,不使用引用计数。

类和结构体的选择

可以选择使用类和结构体来自定义数据类型。然而结构体实例总是通过值传递,类实例总是通过引用传递。

符合下列一个或多个条件时,适合构建结构体:

  • 该数据结构的主要目的是用来封装少量相关简单数据值
  • 有理由预计该数据结构的实例在被赋值或传递时,封装的数据将会被拷贝而不是被引用
  • 该数据结构中存储的值类型属性,也应该被拷贝,而不是被引用
  • 该数据结构不需要去继承另一个既有类型的属性或者行为

定义一个类,生成一个它的实例,并通过引用来管理和传递。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。