swift简单总结(二十)—— 属性

版本记录

版本号 时间
V1.0 2017.07.28

前言

我是swift2.0的时候开始接触的,记得那时候还不是很稳定,公司的项目也都是用oc做的,并不对swift很重视,我自己学了一段时间,到现在swift3.0+已经出来了,自己平时也不写,忘记的也差不多了,正好项目这段时间已经上线了,不是很忙,我就可以每天总结一点了,希望对自己对大家有所帮助。在总结的时候我会对比oc进行说明,有代码的我会给出相关比对代码。
1. swift简单总结(一)—— 数据简单值和类型转换
2. swift简单总结(二)—— 简单值和控制流
3. swift简单总结(三)—— 循环控制和函数
4. swift简单总结(四)—— 函数和类
5. swift简单总结(五)—— 枚举和结构体
6. swift简单总结(六)—— 协议扩展与泛型
7. swift简单总结(七)—— 数据类型
8. swift简单总结(八)—— 别名、布尔值与元组
9. swift简单总结(九)—— 可选值和断言
10. swift简单总结(十)—— 运算符
11. swift简单总结(十一)—— 字符串和字符
12. swift简单总结(十二)—— 集合类型之数组
13. swift简单总结(十三)—— 集合类型之字典
14. swift简单总结(十四)—— 控制流
15. swift简单总结(十五)—— 控制转移语句
16. swift简单总结(十六)—— 函数
17. swift简单总结(十七)—— 闭包(Closures)
18. swift简单总结(十八)—— 枚举
19. swift简单总结(十九)—— 类和结构体

属性

属性将值跟特定的类、结构体或枚举关联,存储属性存储常量或者变量作为实例的一部分,计算属性计算一个值,计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。

存储属性和计算属性通常用于特定类型的实例,但是,属性也可以直接用于类型本身,这种属性称为类型属性。另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作,属性观察器可以添加到自己写的存储属性上,也可以添加到父类继承的属性上。

下面从以下几点进行讲解属性。

  • 存储属性(Store Properties)
  • 计算属性(Computed Properties)
  • 属性观察器(Property Observers)
  • 全局变量和局部变量(Global and Local Variables)
  • 类型属性(Type Properties)

存储属性

一个存储属性就是存储在特定类或者结构体的实例里的一个常量或者变量,存储属性可以是变量存储属性,也可以是常量存储属性。

可以在定义存储属性的时候指定默认值,也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值。

struct FixedLengthRange {
    var firstValue : Int
    let length : Int
}

class JJSwiftVC: UIViewController
{
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        var range = FixedLengthRange(firstValue: 0, length: 3)
        range.firstValue = 10
        print(range.firstValue)
        print(range.length)
    }
}

下面看输出结果

10
3

这里,range实例包含了一个名为firstValue的变量存储属性和一个名为length的常量存储属性,上面例子,length在创建时候被赋值,因为它是常量存储属性,不能后来修改它的值。

1. 常量存储属性

如果创建了一个结构体的实例并赋值给一个常量,则无法修改实例的任何属性,即使定义了变量存储属性。

  let range = FixedLengthRange(firstValue: 0, length: 3)
  //range设置了常量,尽管firstValue为变量,那么也不可以修改
  range.firstValue = 10

这是因为结构体是值类型,当值类型的实例被声明为常量的时候,它的所有属性也就成了常量;而类是引用类型,把一个引用类型实例赋值给一个常量后,仍然可以修改实例的变量属性。

2. 延迟存储属性

延迟存储属性是指第一次被调用的时候才会计算其初始值的属性,在属性声明前使用lazy来标示一个延迟存储属性。

注意:必须将延迟属性声明成变量(var关键字),因为属性的值再实例构造完成之前可能无法得到,而常量属性在构造过程完成之前必须要有初始值,因此不能声明成延迟属性。

当属性的值依赖于在实例的构造过程结束前无法知道具体值的外部因素时,或者当属性的值需要复杂或大量计算时,可以只在需要的时候来计算它,这个时候都用延迟属性。

下面看这里例子。

class JJSwiftVC: UIViewController
{
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        let manager = DataManager()
        manager.data.append("data")
        manager.data.append("here")
        print(manager.data)
    }
}

//该类是一个将外部文件中的数据导入的类,提供了数据导入功能
class DataImporter {
    var fileName = "data.txt"
}

//该类是数据管理功能
class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
}

这里,DataManager类包含一个data的存储属性,是一个空数组,它的功能就是从文件导入数据,该功能由类DataImporter完成,DataManager可能从文件中导入数据,也可能不从中导入数据,所以当DataManager实例被创建时,没必要创建一个DataImporter实例,更好的方式就是用到DataImporter的时候再去创建它。由于使用了lazy,所以importer属性只有第一次被创建的时候才被创建。

3. 存储属性和实例变量

OC中我们可以有属性和实例变量,而swift中把二者统一了起来,swift中的属性没有对应的实例变量,属性的后端存储也无法直接访问,这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。


计算属性

类、结构体和枚举还可以定义计算属性,计算属性不直接存储值,而是提供了一个getter来获取值,一个可选的setter来间接设置其它属性或变量的值。

下面看一个例子。

struct Point {
    var x = 0.0
    var y = 0.0
}

struct Size {
    var width = 0.0
    var height = 0.0
}

struct Rect {
    var origin = Point()
    var size = Size()
    var center : Point{
        get{
            let centerX = origin.x + size.width * 0.5
            let centerY = origin.y + size.height * 0.5
            return Point(x: centerX, y: centerY)
        
        }
        set(newCenter){
            origin.x = newCenter.x - size.width * 0.5
            origin.y = newCenter.y - size.height * 0.5
        }
    }
}

class JJSwiftVC: UIViewController
{
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        var square = Rect(origin: Point(x : 0.0, y : 0.0), size: Size(width: 10.0, height: 10.0))
        let initialCenter = square.center
        
        print(initialCenter)
        square.center = Point(x: 20.0, y: 20.0)
        print("square.origin is at (\(square.origin.x), \(square.origin.y))")
        
    }
}

下面看输出结果

Point(x: 5.0, y: 5.0)
square.origin is at (15.0, 15.0)

这里Rect中的center就是计算属性,

1. 便携setter声明

如果计算属性的setter没有定义表示新值的参数名,则可以使用默认名称newValue,下面是使用了便携setter声明的例子。所以我们的Rect结构体也可以这么写。

struct Rect {
    var origin = Point()
    var size = Size()
    var center : Point{
        get{
            let centerX = origin.x + size.width * 0.5
            let centerY = origin.y + size.height * 0.5
            return Point(x: centerX, y: centerY)
        
        }
        set{
            origin.x = newValue.x - size.width * 0.5
            origin.y = newValue.y - size.height * 0.5
        }
    }
}

2. 只读计算属性

只有getter没有setter的计算属性就是只读计算属性,只读属性总是返回一个值,可以通过点运算访问,不能设置新值,估计大家知道OC中的属性修饰符readonly,它们是类似的。

只读计算属性声明可以去掉get关键字和花括号。看下面的简单例子。

struct Cuboid {
    var width = 0.0
    var height = 0.0
    var depth = 0.0
    var volume : Double {
        return width * height * depth
    }
}

class JJSwiftVC: UIViewController
{
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        let volumeResult = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
        print(volumeResult.volume)
    }
}

下面看输出结果

40.0

这里的体积就是getter直接返回就可以。


属性观察器

属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外,可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加观察器。

注意:不需要为无法重载的计算属性添加属性观察器,因为可以通过setter直接监控和响应值的变化。

可以为属性添加如下的观察器

  • willSet在设置新值之前调用
  • didSet在新的值被设置之后立即调用

willSet观察器会将新的属性值作为固定参数传入,在willSet的代码实现中可以为这个参数指定一个名称,也可以不指定,这时使用默认名称newValue表示。同理。didSet观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名oldValue

注意willSetdidSet观察器在属性初始化过程中不会被调用,它们只会当属性的值在初始化之外的地方被设置时被调用。

下面看个简单例子。

class StepCounter{
    var totalSteps : Int = 0 {
        willSet(newValue){
            print("set totalSteps to \(newValue)")
        }
        didSet(oldValue){
            if totalSteps > oldValue {
                print("Add \(totalSteps - oldValue) steps")
            }
        }
    }
}

class JJSwiftVC: UIViewController
{
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        let stepCounter = StepCounter()
        stepCounter.totalSteps = 200
        stepCounter.totalSteps = 360
        stepCounter.totalSteps = 900
    }
}

下面看输出结果

set totalSteps to 200
Add 200 steps
set totalSteps to 360
Add 160 steps
set totalSteps to 900
Add 540 steps

这里,willSetdidSet都定义了变量,如果不定义就用默认值newValueoldValue

如果在didSet观察器里为属性赋值,这个值会替换观察器之前设置的值。


全局变量和局部变量

计算属性和属性观察器描述的模式也可以用于全局变量和局部变量,全局变量是在函数、方法、闭包或任何类型之外定义的变量,局部变量是在函数、方法或闭包内部定义的变量。在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器。

注意:全局常量或变量都是延迟计算的,跟延迟存储属性相似,不同地方在于,全局的常量或变量不需要标记lazy特性。局部范围的常量或变量不会延迟计算。


类型属性

实例的属性属于一个特定类型实例,每次类型实例化后都拥有自己的一套属性值,实例之间的属性相互独立,也可以为类型本身定义属性,不管类型有多少个实例,这些属性都只有唯一一份,这种属性就是类型属性,类型属性用于定义特定类型所有实例共享的数据。对于值类型(结构体和枚举)可以定义存储型和计算型属性,对于类(class)则只能定义计算型类型属性,值类型的存储类型属性可以是变量或常量,计算型类型属性和实例的计算属性一样定义成变量属性。

注意:跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。

1. 类型属性语法

COC中,静态常量和静态变量的定义是通过特定类型加上global关键字,在swift中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。利用关键字static来定义值类型的类型属性,关键字class来为类定义类型属性。

看下面几个例子。

struct SomeStruct {
    static var storedTypeProperty = "Some value"
    static var computedTypeProperty : Int {
        return 100
    }
}

enum SomeEnumeration {
    static var storedTypeProperty = "Some value"
    static var computedTypeProperty : Int {
        return 150
    }
}

class SomeClass {
    class var  computedTypeProperty : Int {
        return 200
    }
}

2. 获取和设置类型属性的值

跟实例的属性一样,类型属性的访问也是通过点运算来运行,但是,类型属性是通过类型本身来获取和设置,而不是通过实例,比如:

class JJSwiftVC: UIViewController
{
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        print(SomeClass.computedTypeProperty)
        print(SomeStruct.storedTypeProperty)
        SomeStruct.storedTypeProperty = "Another value"
        print(SomeStruct.storedTypeProperty)
    }
}

下面看输出结果

200
Some value
Another value

再看一下下面的例子。

struct AudioChannel {
    static let thresholdLevel = 10
    static var maxInputLevelForAllChannels = 0
    var currentLevel : Int = 0 {
        didSet{
            if currentLevel > AudioChannel.thresholdLevel {
                currentLevel = AudioChannel.thresholdLevel
            }
            if currentLevel > AudioChannel.maxInputLevelForAllChannels {
                AudioChannel.maxInputLevelForAllChannels = currentLevel
            }
        }
    }
}

下面我们调用下代码,查看结果。

class JJSwiftVC: UIViewController
{
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        var leftChannel = AudioChannel()
        var rightChannel = AudioChannel()
        leftChannel.currentLevel = 7
        print(leftChannel.currentLevel)
        print(AudioChannel.maxInputLevelForAllChannels)
        
        rightChannel.currentLevel = 11
        print(rightChannel.currentLevel)
        print(AudioChannel.maxInputLevelForAllChannels)
    }
}

下面看输出结果

7
7
10
10

后记

未完,待续~~~~

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

推荐阅读更多精彩内容