Swift的属性

属性分类
在Swift中, 严格意义上来讲属性可以分为两大类: 实例属性和类型属性

实例属性(Instance Property): 只能通过实例去访问的属性
存储实例属性(Stored Instance Property): 存储在市里的内存中, 每个实例都只有一份
计算实例属性(Computed Instance Property)
类型属性(Type Property): 只能通过类型去访问的属性
存储类型属性(Stored Type Property): 整个程序运行过程中就只有一份内存(类似全局变量)
计算类型属性(Computed Type Property)
类型属性可以通过static关键字定义; 如果是类也可以通过class关键字定义
实例属性属于一个特定类型的实例,每创建一个实例,实例都拥有属于自己的一套属性值,实例之间的属性相互独立
为类型本身定义属性,无论创建了多少个该类型的实例,这些属性全局都只有唯一一份,这种属性就是类型属性
实例属性
上面提到Swift中相关的属性可以分为两大类:存储属性和计算属性

存储属性(Stored Property)
类似于成员变量,系统会为其分配内存空间,存储属性存储在实例的内存中
存储属性可以是变量存储属性(用关键字var定义),也可以是常量存储属性(用关键字let定义)
结构体和类可以定义存储属性, 枚举不可以定义存储属性
计算属性(Computed Property)
计算属性其本质就是方法(函数), 系统不会为其分配内存空间, 所以计算属性不会占用- 实例对象的内存
计算属性不直接存储值,而是提供一个getter和一个可选的setter,来间接获取和设置其他属性或变量的值
存储属性
在Swift中存储属性可以是var修饰的变量, 也可以是let修饰的常量
但是在创建类或结构体的实例时, 必须为所有的存储属性设置一个合适的初始值, 否则会报错的
可以在定义属性的时候, 为其设置一个初始值
可以在init初始化器里为存储实行设置一个初始值
struct Person {
// 定义的时候设置初始值
var age: Int = 24
var weight: Int
}
// 使用init初始化器设置初始值
var person1 = Person(weight: 75)
var person2 = Person(age: 25, weight: 80)
上面两个属性是会占用实例的内存空间的
可以使用MemoryLayout获取数据类型占用的内存大小
// Person结构体实际占用的内存大小
MemoryLayout<Person>.size // 16
// 系统为Person分配的内存大小
MemoryLayout<Person>.stride // 16
// 内存对其参数
MemoryLayout<Person>.alignment // 8
有一种使用方式, 输出结果一致

var person = Person(weight: 75
MemoryLayout.size(ofValue: person)
MemoryLayout.stride(ofValue: person)
MemoryLayout.alignment(ofValue: person)
计算属性
计算属性不直接存储值,而是提供一个getter和一个可选的setter,来间接获取和设置其他属性或变量的值
计算属性其本质就是方法(函数), 系统不会为其分配内存空间, 所以计算属性不会占用实例对象的内存
struct Square {
var side: Int
var girth: Int {
set {
side = newValue / 4
}
get {
return side * 4
}
}
}

// 其中set也可以使用下面方式
set(newGirth) {
side = newGirth / 4

下面我们先看一下Square所占用的内存大小, 这里方便查看都去掉了print函数

var squ = Square(side: 4)

MemoryLayout.size(ofValue: squ) // 8
MemoryLayout.stride(ofValue: squ) // 8
MemoryLayout.alignment(ofValue: squ) // 8

从上面输出结果可以看出,Square只占用8个内存大小, 也就是一个Int占用的内存大小, 如果还是看不出来, 可以看一下下面这个

struct Square {
var girth: Int {
get {
return 4
}
}
}

// 输出结果0
print(MemoryLayout<Square>.size) // 0

从上面两个输出结果可以看出, 计算属性并不占用内存空间
此外, 计算属性虽然不直接存储值, 但是却需要get、set方法来取值或赋值
其中通过set方法修改其他相关联的属性的值; 如果该计算属性是只读的, 则不需要set方法, 传入的新值默认值newValue, 也可以自定义
通过get方法获取该计算属性的值, 即使是只读的, 计算属性的值也是可能发生改变的
定义计算属性只能使用var, 不能使用let
延迟存储属性
使用lazy可以定义一个延迟存储属性(Lazy Stored Property), 延迟存储属性只有在第一次使用的时候才会进行初始化
lazy属性修饰必须是var, 不能是let
let修饰的常量必须在实例的初始化方法完成之前就拥有值
class Car {
init() {
print("Car init")
}

func run() {
    print("Car is runing")
}

}

class Person {
lazy var car = Car()
init() {
print("Person init")
}

func goOut() {
    car.run()
}

}

let person = Person()
print("--------")
person.goOut()

// 输出结果
// Person init
// --------
// Car init
// Car is runing

上述代码, 在初始化car的时候如果没有lazy, 则输出结果如下

/*
Car init
Person init


Car is runing
*/

这也就证明了延迟存储属性只有在第一次使用的时候才会被初始化
此外还有一种复杂的延迟存储属性, 有点类似于OC中的懒加载
属性观察器
在Swift中可以为非lazy的并且只能是var修饰的存储属性设置属性观察器, 形式如下

struct Person {
var age: Int {
willSet {
print("willSet", newValue)
}
didSet {
print("didSet", oldValue, age)
}
}

init() {
    self.age = 3
    print("Person init")
}

}

var p = Person()
p.age = 10
print(p.age)

/* 输出结果
Person init
willSet 10
didSet 3 10
10
*/

在存储属性中定义willSet或didSet观察者,来观察和响应属性值的变化, 从上述输出结果我们也可以看到
willSet会传递新值, 在存储值之前被调用, 其默认的参数名是newValue
didSet会传递旧值, 在存储新值之后立即被调用, 其默认的参数名是oldValue
当每次给存储属性设置新值时,都会调用属性观察者,即使属性的新值与当前值相同
在初始化器中设置属性和在定义属性是设置初始值都不会触发willSet或didSet
类型属性
存储类型属性(Stored Type Property): 整个程序运行过程中就只有一份内存(类似全局变量)
计算类型属性(Computed Type Property): 不占用系统内存
类型属性可以通过static关键字定义; 如果是类也可以通过class关键字定义
存储类型属性可以声明为变量或常量,计算类型属性只能被声明为变量
存储类型属性必须设置初始值, 因为存数类型属性没有init初始化器去设置初始值的方式
存储类型属性默认就是延迟属性(lazy), 不需要使用lazy修饰符标记, 只会在第一次使用的时候初始化, 即使是被多个线程访问, 也能保证只会被初始化一次
// 在结构体中只能使用static
struct Person {
static var weight: Int = 30
static let height: Int = 100
}

// 取值
let a = Person.weight
let b = Person.height

// 赋值
Person.weight = 12
// let修饰的不可被赋值
//Person.height = 10

在类中可以使用static和class

class Animal {
static var name: String = "name"
class var age: Int {
return 10
}
}

// 取值
let a1 = Animal.name
let a2 = Animal.age

// 赋值
Animal.name = "animal"
// class定义的属性是只读的
// Animal.age = 20

static
可以修饰class、struct、enum类型的属性或者方法
被修饰的class中的属性和方法不可以在子类中被重写, 重写会报错
修饰存储属性
修饰计算属性
修饰类型方法
struct Person {
// 存储属性
static var weight: Int = 30
// 计算属性
static var height: Int {
get { 140 }
}
// 类型方法
static func goShoping() {
print("Person shoping")
}
}

class
只能修饰类的计算属性和方法
不能修饰类的存储属性
修饰的计算属性和方法可以被子类重写

class Animal {
// 计算属性
class var height: Int {
get { 140 }
}
// 类型方法
class func running() {
print("Person running")
}
}

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

推荐阅读更多精彩内容