Swift初学 - 四大自定义数据类型之Struct

Swift有四种自定义数据类型:structures, classes, enumerations和protocols。今天来详细介绍一下Struct,我们平时用的Int, String, Array, Dictionary等也都是以struct的形式定义的

//From Swift Library
public struct Int : FixedWidthInteger, SignedInteger {
 // …
}

一、Structure的声明与instance创建

1、声明

假设我们要为公司做一个员工统计:

struct Person {
  let firstName: String
  let lastName: String
}

这里我们声明了一个Struct “Person”,每个person都有名字和姓,因为人的名字一般不会改,所以firstName和lastName都用了let(声明为constant)。公司的员工除了姓名外,至少还应该有个职称

struct Employee {
  let identity: Person
  var jobTitle: String
  var salary: Double
}

这里identity的type是我们刚定义的Person,职称和薪资因为有可能会变,jobTitle、salary用var来声明。

2、创建instance

如果要创建一个Person的instance,我们可以用swift为struct自动生成的initializer

let me = Person(firstName: "Joshua", lastName: "Jiang")

值得注意的是,如果你要用这个自动生成的initializer,必须要在创建instance的时候把所有properties都赋值才行,即使你在声明的时候给了默认值(除非这个属性的类型是optional)。如果我们想创建一个Employee就可以写成

var joshua = Employee(identity: me, jobTitle: "CTO", salary: 3000)

注意因为职称可能会变,如果我们用let声明了joshua,以后他变ceo的时候,即使jobTitle是变量,修改它也会出error

let joshua = Employee(identity: me, jobTitle: "CTO", salary: 3000)
joshua.jobTitle = "CEO" //Error: cannot assign to property

二、Properties属性

1、stored properties和computed properties

我们刚刚定义的Person和Employee下的firstName、lastName、jobTitle等都属于stored property,他们都储存了我们赋给他们的值,是占用内存空间的;但是还有一种computed properties是随时用随时计算出来的,并不占用任何内存空间,没有值被储存。举个例子:

struct Employee {
  let identity: Person
  var jobTitle: String
  var salary: Double

  var tax: Double {
    return salary * 0.05
  }
}

我们在Employee类型里加了一个新的变量tax,代表这个员工需要交的税。如果税只需要工资就能计算出来,那么如果我们把tax声明成一个stored property,然后每次创建一个Employee的时候还需要先人工计算出他的tax再赋值,那就太傻了;这个时候computed property最适合。注意computed property必须是用var来声明。

var joshua = Employee(identity: me, jobTitle: "CTO", salary: 3000)
let tax = joshua.tax //150.0

上述例子中的tax是一个只读computed property,有需要的话我们也可以定义成可读可写的

var tax: Double {
  get {
    return salary * 0.05
  }
  set {
    salary = newValue / 0.05
  }
}

因为我们想让tax变成可读可写,那就需要自定义get和set这两个方法(如果是只读,swift会自动帮我们加上get)。这样的话,如果我们给tax赋值,salary就会相应的改变(在现实生活中可能没什么意义)。newValue是自动传入的参数,代表tax新被赋予的值。

joshua.tax = 300
print(joshua.salary) //6000.0

2、type properties

computed property和stored property都是instance的属性,数据类型也有自己的属性,这就是type property。比如:

struct Employee {
  static var location = "青岛"
  let identity: Person
  var jobTitle: String
  var salary: Double
}

location前面加了static,这就意味着location是Employee这个类型的属性,不用创建instance就可以读取。在实践中有时候会很有用。

let location = joshua.location //Error: you can't access a type property on an instance
let location = Employee.location //正确用法

3、property observers

store property是有observer观察者的,willSet会在属性即将更新的时候执行;didSet会在属性刚更新完的时候执行。假设薪水突然翻番儿了,那就是location变了(现实逻辑瞎扯,看懂代码逻辑就行)

var salary: Double {
  didSet {
    if salary >= oldValue * 2 {
      Employee.location = "北京"
    }
  }
}

a) didSet中,salary已经是更新之后的值了
b) 改变之前的值可以用oldValue来读取更新之前的值
c) 为了区别于其他属性,你需要用Employee.location来说明location是type property
d) didSet和willSet只能用于stored property,你想监听computed property的话,直接在他们的set里实现相应逻辑就行了
e) 在创建Employee instance的时候,didSet和willSet并不会执行,只有更新的时候才会
f) 读完e),也就说明只有var声明的变量才可以用didSet和willSet

三、Struct中的methods

1、定义

首先如果一个func定义在struct的外面,那他就是个function,如果定义在struct的里面那他就是method。但因为我们已经有了computed property, 我们在需要大量计算或者需要读写数据库的时候用methods,其他情况一般用computed property就可以了。

let jobTitles = ["Developer", "CTO", "COO", "CFO", "CEO"]

struct Employee {
  let identity: Person
  var jobTitle: String
  var salary: Double

  func roomFor() -> String {
        guard let index = jobTitles.firstIndex(of: self.jobTitle) else {
            return "Unknow"
        }
        return "00\(index)"
    }
}

我们定义了一个method来获取员工的房间号码,这个很简单,一个小地方说明一下,因为roomFor是在struct内部定义的method,self完全可以省略。

var joshua = Employee(identity: me, jobTitle: "CTO", salary: 3000)
joshua.roomFor() // "000"

2、自定义init()

我们之前创建instance的时候用的都是系统生成的init,但我们公司现在只缺月薪2000的程序员了,创建的时候每次输入同样的值太麻烦。这时候我们就可以自定义init()。

struct Employee {
  let identity: Person
  var jobTitle: String
  var salary: Double

  init(identity: Person) {
    self.identity = identity
    jobTitle = "Developer"
    salary = Double(2000.0)
  }
}

var joshua = Employee(identity: me) //简单多了

a) 有了自定义的init,之前自动生成那个就不能用了哦,需要的话再自己写一个
b) 这里的self是需要的,因为要区别于local variable

3、mutating methods

struct里的method是不能改变属性的值的,除非定义的时候在前面加mutating

mutating func raise() {
  salary += 100
}

a) 我们知道swift中,function一般是不能改变传入参数的值的,除非传入参数被标明inout;有了mutating,swift会帮我们偷偷把self标明inout的
b) 如果一个instance想要执行raise,那他一定要是一个var声明的变量

4、type methods

和type property一样,类型可以有自己的type method,比如

let jobTitles = ["CTO", "COO", "CFO", "CEO"]

struct Employee {
    let identity: Person
    var jobTitle: String
    var salary: Double
    
    static func salaryOf(jobTitle: String) -> Double {
        guard let index = jobTitles.firstIndex(of: jobTitle) else {
            return 0.0
        }
        return Double(3000) * Double(index + 1)
    }
}

Employee.salaryOf(jobTitle: "CEO") //12000

四、Extension

假设Employee是个第三方库,我们不能改源码,这时候就可以通过extension来填加自定义method:

extension Employee {
  init(identity: Person) {
    self.identity = identity
    jobTitle = "Developer"
    salary = Double(2000.0)
  }
}

我们用extension加了一个自定义init,跟之前在struct里面加的区别是,用extension可以保留系统自动生成的那个init。

需要注意的是:
a) extension不能用来加store property,因为这样会更改已有struct内存占用大小,破坏已有代码。
b) extension不能override原struct已有的method

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

推荐阅读更多精彩内容

  • 在实际编程中,很多时候,我们都需要使用比Int,String这类简单类型更复杂的类型,例如,需要两个Double表...
    AKyS佐毅阅读 1,833评论 0 6
  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,718评论 2 9
  • { "Unterminated string literal.": "未终止的字符串文本。", "Identifi...
    Elbert_Z阅读 10,738评论 0 2
  • 当它悄悄从我的记忆里停留 并买走我所有倒霉的往事 留给我很多梦寐以求的快乐 幻想的觉醒 我前所未有的初恋 幻想的...
    爱情咨询师阅读 199评论 0 1
  • 我有一个朋友非常需要一封斯坦福教授的推荐信。他写作业的时候一直非常没有信心,因为作业真的很难。好在连续两周作业下来...
    MierCat阅读 151评论 1 2