Swift语法学习笔记4:结构体与类

本篇是《Swift语法学习笔记》系列的第四篇文章,将涵盖以下内容:

  • 结构体
- 定义与使用结构体
- 结构体的初始化方法
      • 一个简单的类
      • 属性
      • 方法
    • 继承
      • 子类生成
      • 重写属性
      • 重写属性观察器
      • 防止重写

一段很重要很重要的话

Swift provides two different ways to create custom types—classes and structures. Both may contain properties, initializers, and methods. A property is a variable defined within the body of a class or structure; a method is a function defined in a class or structure; and an initializer is a special type of method that is used to establish the initial state of a class or structure instance while it’s being created.
There are two main differences between classes and structures:

  • Classes are reference types, whereas structures are value types. That means that when you pass an instance of a structure to a function, return an instance from a function, or assign the value of a variable that refers to a structure to another variable, the instance is copied. In other words, structures exhibit pass-by-value behavior. Swift strings, arrays, sets and dictionaries are all implemented as structures. By contrast, class instances are passed by reference—no copy is taken
  • Classes can be subclassed to add behavior; structures cannot.

结构体

1 定义与使用结构体

编译器会自动创建一个包含初始化参数的默认的初始化函数:

struct CircleStruct {
    var radius: Double
    func getArea() -> Double {
        return M_PI * radius * radius
    }
    func getCircumference() -> Double {
        return 2 * M_PI * radius
    }
}
var circleStruct = CircleStruct(radius: 10)
let r = circleStruct.radius     // Reads the radius property – result = 10
circleStruct.radius = 2 * r     // Doubles the radius

验证拷贝特性:

var newCircleStruct = circleStruct   // Copies the structure
newCircleStruct.radius = 32          // Affects only the copy
newCircleStruct.radius               // New value: 32
circleStruct.radius                  // Old value: 20

如果把一个结构体赋值给一个常量,那么他的所有属性都转变为只读:

let constantCircleStruct = CircleStruct(radius: 5)
constantCircleStruct.radius = 10     // Invalid: constantCircleStruct is constant

2 结构体的初始化方法

struct CircleStruct {
    var radius: Double = 1
    //假如用户有自定义的初始化函数,那么编译器就不会为其再自动创建初始化函数。
    //若想得到一个不含参数的初始化函数,必须自己手动创建
    
    init() {
    }
    
    init(radius: Double) {
        self.radius = radius
    }
    
    func getArea() -> Double {
        return M_PI * radius * radius
    }
    
    func getCircumference() -> Double {
        return 2 * M_PI * radius
    }
}
let circleStructDefault = CircleStruct()
circleStructDefault.radius         // Result is 1

我们可以修改初始化方法为:

init(_ radius: Double) {
    self.radius = radius
}

这样,我们就可以不指定参数的名称:

var circleStruct = CircleStruct(10)     // Argument name must be omitted

1 类

1.1 一个简单的类
class CircleClass {
    var radius: Double = 1
    
    init() {
    }

    init(radius: Double) {
        self.radius = radius
    }
    
    func getArea() -> Double {
        return M_PI * radius * radius
    }
    
    func getCircumference() -> Double {
        return 2 * M_PI * radius
    }
}
var circleClassDefault = CircleClass()    // Sets the default radis
circleClassDefault.radius                 // Result is 1
var circleClass = CircleClass(radius: 10) // Explicitly set the radius
circleClass.radius                        // Result is 10

验证引用特性:

var newCircleClass = circleClass // Does not copy
newCircleClass.radius = 32       // Only one copy, so this change is visible...
newCircleClass.radius            // ...through both refences. Result is 32
circleClass.radius               // Result is 32
1.2 属性

上一小节的例子中的半径是一个存储属性,此外我们还有计算属性,为了说明计算属性,我们将上一小节的例子修改为如下所示:

class CircleClass {
    var radius: Double = 1
    var area: Double {
        return M_PI * radius * radius
    }
    
    var circumference: Double {    
        return 2 * M_PI * radius
    }
    
    init() {
    }
    
    init(radius: Double) {
        self.radius = radius
    }
}

Double后面的{}本质上是一个代码块,我们像使用普通的属性一样使用area和circumference:

let circleClass = CircleClass(radius: 10)
circleClass.area
circleClass.circumference

实际上,上面例子中的area与circumference是简写形式,起具体代码如下所示,由详细代码可以得出:这是一个只读的计算属性:

var area: Double {
    get {
        return M_PI * radius * radius
    }
}

我们可以将其修改为可读写的计算属性:

var area: Double {
    get {
        return M_PI * radius * radius
    }
    set(value) {
        radius = sqrt(value/M_PI)
    }
}

var circumference: Double {
    get {
        return 2 * M_PI * radius
    }
    set(value) {
        radius = value/(2 * M_PI)
    }
}

使用方法:

circleClass.area = 314
circleClass.radius        // Radius for area 314 is 9.997
circleClass.circumference = 100
circleClass.radius        // Radius for circumference 100 is 15.915

Swift支持属性变化的观察。willSet代码块在新的变量值赋值之前被调用,didSet代码块在新的变量值赋值之后被调用。

class CircleClass {
    var radius: Double = 1 {
        didSet {
            if (radius < 0) {
                radius = oldValue
            }
        }
    }
}
circleClass.radius = 10   // Valid: radius set to 10
circleClass.radius        // Result: 10.0
circleClass.radius = -1   // Invalid – trapped by didSet block
circleClass.radius        // Result: 10.0
1.3 方法
func adjustRadiusByAmount(amount: Double, times: Int = 1) {
    radius += amount * Double(times)
}
var circleClass = CircleClass(radius: 10)
circleClass.radius                          // Result: 10
circleClass.adjustRadiusByAmount(5, times: 3)
circleClass.radius                          // Result = 10 + 3 * 5 = 25
circleClass.adjustRadiusByAmount(5)         // "times" is defaulted to 1
circleClass.radius                          // Result = 30    

2 继承

2.1 子类生成
  • Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。
  • 每个类都有一个构造器,它用于创建某个类型的一个新实例。尽管构造器并不是方法,但在语法上,两者很相似。构造器的工作是准备新实例以供使用,并确保实例中的所有属性都拥有有效的初始化值。
    创建如下所示的父类:

    class CircleClass {
    var radius: Double = 1 {
    didSet {
    if (radius < 0) {
    radius = oldValue
    }
    }
    }

      var area: Double {
          get {
              return M_PI * radius * radius
          }
          set {
              radius = sqrt(newValue/M_PI)
          }
      }
    
      var circumference: Double {
          get {
              return 2 * M_PI * radius
          }
          set {
              radius = newValue/(2 * M_PI)
          }
      }
    
      var description: String {
          return "Circle of radius \(radius)"
      }
      
      required init() {
      }
      
      init(radius: Double) {
          self.radius = radius
      }
    
      func adjustRadiusByAmount(amount: Double, times: Int = 1) {
          radius += amount * Double(times)
      }
    
      func reset() {
          radius = 1;
      }
    

    }

子类生成(Subclassing),读作:定义一个新的类叫ColoredCircleClass,他继承了CircleClass。

  • 在Swift 中,构造器默认是不继承的。
  • 子类只允许修改从超类继承来的变量属性,而不能修改继承来的常量属性。

  • 任何缺少override关键字的重写都会在编译时被诊断为错误。override关键字会提醒 Swift 编译器去检查该类的超类(或其中一个父类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。

    class ColoredCircleClass : CircleClass {
    var color: UIColor = UIColor.blackColor()
    required init() {
    super.init()
    }
    init(radius: Double, color: UIColor) {
    self.color = color
    super.init(radius: radius)
    }
    override var description: String {
    return super.description + ", color (color)"
    }
    override func reset() {
    super.reset()
    color = UIColor.blackColor()
    }
    }

2.2 重写(Overriding)属性

可以将一个继承来的只读属性重写为一个读写属性,只需要在重写版本的属性里提供 getter 和 setter 即可。但是不可以将一个继承来的读写属性重写为一个只读属性。
如果你在重写属性中提供了 setter,那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接返回super.someProperty来返回继承来的值。

//父类
class Vehicle { 
    var numberOfWheels: Int 
    var maxPassengers: Int 
    func description() -> String { 
        return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers" 
    } 
    init() { 
        numberOfWheels = 0 
        maxPassengers = 1 
    }
}
//子类
class Car: Vehicle { 
    var speed: Double = 0.0 
    init() { 
        super.init() 
        maxPassengers = 5 
        numberOfWheels = 4 
    } 
    override func description() -> String { 
        return super.description() + "; " 
            + "traveling at \(speed) mph" 
    }
}
//子类-重写属性
class SpeedLimitedCar: Car { 
    override var speed: Double  { 
    get { 
        return super.speed
    } 
    set { 
        super.speed = min(newValue, 40.0)
    }
    }
}
2.3 重写属性观察器

你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供willSet或didSet实现是不恰当。此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已经为那个属性提供了定制的 setter,那么你在 setter 中就可以观察到任何值变化了。

class AutomaticCar: Car { 
    var gear = 1 
    override var speed: Double { 
    didSet { 
        gear = Int(speed / 10.0) + 1 
    } 
    } 
    override func description() -> String { 
        return super.description() + " in gear \(gear)" 
    } 
} 

测试

let automatic = AutomaticCar() 
automatic.speed = 35.0 
println("AutomaticCar: \(automatic.description())")     // AutomaticCar: 4 wheels; up to 5 passengers; traveling at 35.0 mph in gear 4
2.4 防止重写

通过把方法,属性或附属脚本标记为final来防止它们被重写,只需要在声明关键字前加上@final特性即可。例如:@final var, @final func, @final class func, 以及@final subscript。

参考文献

《Beginning iPhone Development with Swift 2 - Exploring the iOS SDK 》From page 777 to page 838.

联系作者

你可能感兴趣的:(Swift语法学习笔记4:结构体与类)