属性(Properties)

属性将值跟特定的类、结构或枚举关联。

  • 存储属性: 存储常量或变量作为实例的一部分。
    • 计算属性可以用于 结构体枚举
  • 计算属性: 计算(不是存储)一个值。
    • 存储属性只能用于 ** 类** 和 结构体。*

属性分为 实例属性类型属性

1. 存储属性

存储属性:就是存储在特定类或结构体实例里的一个 常量变量

  • 变量存储属性(用关键字 var 定义)
  • 常量存储属性(用关键字 let 定义)

** 存储属性的默认值 **

  • 存储属性可以在定义的时候赋值默认值。
  • 可以在构造过程中设置和修改存储属性的值。(常量的值只可以在构造函数中进行修改)

常量结构体的存储属性
创建了一个结构体的实例并将其赋值给一个常量,则无法修改该实例的任何属性,即使有属性被声明为变量也不行。
(常量结构体的变量属性是不能被修改的,这个是由于结构体的值类型决定)

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

  • 延迟存储属性必须用 var 声明
  • 被标记为 lazy 的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。(加锁)

实例属性使用场景

  • 属性值对实例的构造过程不依赖
  • 属性的值需要经过大量计算才可以得到(耗时)
    (加载本地文件数据就可以使用延迟属性)

2. 计算属性

  • ** 计算属性不直接存储值,**而是提供一个 getter 和一个可选的 setter,来间接获取和设置其他属性或变量的值。
  • 计算属性只能用 var 来声明。(由于计算属性值的不确定性决定)
struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()

    // 计算属性, 中心点是依赖 origin 和 size 计算得出具体的值。本身不能保存值。
    var center: Point {

        // getter
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }

        // setter (可选的)
        /**
          set 方法默认有一个参数  newValue 用来表示我们传入的值。
          我可以对set 的参数名称进行进行修改,增强代码的可读性。

          这个是 编辑 set 声明的使用,直接使用 newValue 并且 set 的参数可以省略。
          set {
              origin.x = newValue.x - (size.width / 2)
              origin.y = newValue.y - (size.height / 2)
          }
        */
        set(newCenter) {
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
}

只读计算属性:
只有 getter 没有 setter 的计算属性就是只读计算属性。
(只读计算属性总是返回一个固定的值,不能赋新值)

struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    
    // 只读计算属性,可以省略 get 个花括号
    var volume: Double {
        return width * height * depth
    }
}

3. 属性观察器(存储属性和计算属性都可以添加属性观察器)

属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同。

  • 延迟存储属性 不能有 属性观察器。(重点)
  • 可以通过 重写 属性的方式为 继承的属性(包括存储属性和计算属性)添加属性观察器。
  • 可以给所有的存储属性添加属性观察器。
  • 不必为 非重写 (非继承) 的 ** 计算属性 ** 添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。

理论上属性观察器是可以给所有的属性添加。(不用区分是否是存储是否是计算。主要是要区分一下使用的场景)

可以为属性添加如下一个或多个属性观察器:

  • willSet: 在新的值被设置之前调用
    • willSet 观察器会将新的属性值作为 常量参数 传入。
      在 willSet 的实现代码中可以为这个参数指定一个名称,不指定可以使用默认参数 newValue。
  • didSet: 在新的值被设置之后调用
    • didSet 观察器会将旧值作为参数传入。
      在 didSet 的实现代码中可以为这个参数指定一个名称, 不指定可以使用默认的参数 oldValue
class StepCounter {

    // 这是一个存储属性
    var totalSteps: Int = 0 {

        // willSet 属性观察器 , 指定的传入参数名称为 newTotalSteps。
        /*
        // 这个为使用默认参数
        willSet {
            print("About to set totalSteps to \(newValue)")
        }
        */
        willSet(newTotalSteps) {
            print("About to set totalSteps to \(newTotalSteps)")
        }


        /*
        // 自己定义一个参数使用,主要是为了代码的语义化。
        didSet ( tempOldValue ) {
            // totalSteps 就是属性本身。
            if totalSteps > tempOldValue  {
                print("Added \(totalSteps - tempOldValue) steps")
            }
        }
        */
        // 这个为使用默认参数
        didSet {
            if totalSteps > oldValue  {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}

存储属性的使用注意:

  • 父类的属性在子类中赋值时,父类的 属性观察器会先被调用,后才会调用子类的属性观察器。
    • 在父类初始化方法调用之前,子类给属性赋值时,观察器不会被调用。
  • 属性是通过 in-out 方式传入, 属性观察器也会被调用。
 * in-out 是用的是值的拷贝使用, 使用的是值的拷贝,值使用完毕后再将值拷贝出来。

4. 全局变量 和 局部变量

全局变量:是在函数、方法、闭包或任何类型之外定义的变量。
局部变量:是在函数、方法或闭包内部定义的变量。

全局或局部变量都属于存储型变量,跟存储属性类似,它为特定类型的值提供存储空间,并允许读取和写入。

在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器。计算型变量跟计算属性一样,返回一个计算结果而不是存储值,声明格式也完全一样。

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

5. 类型属性

实例属性: 特定类型的实例的属性叫实例属性。实例之间的属性是相互独立的。
类型属性:特定类型的属性叫实例属性。类型之间的属性是共享的。只有唯一一份。
(类型属性: 特么第一眼我也没看明白。其实就是特么写在类里面的静态变量和静态常量)

使用 static 来定义类型属性(静态变量和静态常量)。

定义计算型类型属性,可以改用 class 来支持子类对父类实现进行重写。

struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 1
    }
}
enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 6
    }
}
class SomeClass {
    static var storedTypeProperty = "Some value."
    
    // 使用 static 表示不允许子类重写 
   static var computedTypeProperty: Int {
        return 27
    }

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

推荐阅读更多精彩内容

  • 属性就是将值和特定的类、结构和枚举进行关联。存储属性存储常量(变量)来作为实例的一部分,而计算属性不是存储一个值。...
    Mustard_Buli阅读 2,084评论 0 2
  • 计算属性可以用于类、结构体和枚举, 存储属性只能用于类和结构体。 枚举只能有计算属性 属性观察器可以添加到自己定义...
    勇往直前888阅读 568评论 0 0
  •  属性:将值与特定的类、结构体或枚举关联; 存储属性:存储常量或变量作为实例的一部分; 计算属性:用于计算一个值(...
    EndEvent阅读 613评论 1 5
  • 常量与变量使用let来声明常量,使用var来声明变量。声明的同时赋值的话,编译器会自动推断类型。值永远不会被隐式转...
    莫_名阅读 433评论 0 1
  • Swift语法基础(五)-- (类和结构体、属性、方法) 本章将会介绍 类和结构体对比结构体和枚举是值类型类是引用...
    寒桥阅读 1,078评论 0 1