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.

联系作者

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容