Swift 属性类型以及单例模式

属性类型

  • Swift中的属性

    1. 存储属性
    2. 计算属性
  • 存储属性

    1. 类似于成员变量的概念
    2. 存储在实例的内存中
    3. 结构体/类可以存储存储属性
    4. 枚举不可以定义存储属性

    在创建类或者结构体的时候,必须为所有的存储属性设置一个合适的初始值

    1. 可以在初始化器中设置一个初始值
    2. 可以分配一个默认的属性值作为属性定义的一部分
  • 计算属性

    1. 本质就是方法
    2. 不占用实例变量的内存
    3. 枚举、结构体、类都可以定义计算属性

    set传入的新值默认叫做newValue,也可以自定义。

    1. 定义计算属性只能用var,不能用let
    2. 计算属性的值是可能发生变化的(即便是只读计算属性)

计算属性中还有一种属性叫做只读计算属性(只有get,没有set

func perprotyUse() {
        
        //结构体占用了8个字节的内存空间
        struct Cirle {
            
            //存储属性
            var radius: Double
            
            //计算属性
            var diameter: Double {
                set{
                    radius = newValue / 2
                }
                get{
                    radius * 2
                }
            }
            
            //只读计算属性
            var infoDes: String {
                get{
                    "这里只有只读,没有计算"
                }
            }
        }
        
        //自定义set的默认值,默认名字叫做newValue
        struct testCircle{
            var radius: Double
            var diameter: Double{
                get{
                    radius * 2
                }
                set(someValue){
                    radius = someValue/2
                }
            }
        }
    }
  • 枚举rawValue原理
    枚举的原始值rawValue的本质是只读计算属性,实现方法类似于下面的方法
func enumRawValue() {
        enum TestEnum: Int{
            case test1 = 1, test2 = 2, test3 = 3
            var rawValue: Int{
                switch self {
                case .test1: return 10
                case .test2: return 20
                case .test3: return 30
                }
            }
        }
    }
  • 延时存储属性

    1. 使用lazy可以定义延时存储属性,第一次用到属性的时候会进行初始化,类似于oc中的懒加载
    2. lazy属性必须是var类型,不能是letlet要求所有的属性都在使用之前进行初始化)
    3. let必须在实例的初始化方法完成之前就拥有值
    4. 如果多跳线程同时第一次访问lazy属性,无法保证属性只被初始化1次,也就是说延时属性并不是线程安全的

    延迟存储属性需要注意的点

    当结构体包含一个延迟存储属性的时候,只有var才能访问延迟存储属性,因为延迟属性初始化的时候需要改变结构体的内存

class Car {
        init() {
            print("car init")
        }
        func run() {
            print("car is running")
        }
    }
    
    class Person {
        lazy var car: Car = Car()
        init() {
            print("person init")
        }
        
        func goOut() {
            car.run()
        }
    }
    
    class lazyLoad {
        lazy var loadStr: NSString = {
            return "这是懒加载"
        }()
    }

    struct Point {
        var x = 0
        var y = 0
        lazy var z = 0
    }
    func usePointStruct() {
        let p = Point()
        //以下句子会报错
//        print(p.z)
    }

  • 属性观察器
    1. 为非lazyvar存储属性设置属性观察器
    2. willSetdidSet在存储属性里面用到的,不是在计算属性中
    3. willSet会传递新值,默认叫newValue
    4. didSet会传递旧值,默认叫oldValue
    5. 在初始化器中设置属性值不会触发willSetdidSet
    6. 在属性定义时设置初始值也不会触发willSetdidSet

全局变量、局部变量的应用

  1. 属性观察器、计算属性的功能,也可以应用在全局变量、局部变量身上
  2. 属性观察器的本质:在进行修改的时候,会声明一个中间变量并修改局部变量的值,最后调用set方法设置到属性中去的
struct Circle {
        var radius: Double{
            willSet {
                print("willSet", newValue)
            }
            
            didSet {
                print("didSet", oldValue)
            }
        }
    }
  • inout的本质

    1. 如果实参有物理内存地址,且没有设置属性观察期,直接将实参的内存地址传入函数(实参进行引用传递)
    2. 如果实参是计算属性或者设置了属性观察器,采取 Copy In Copy Out的做法

    Copy In Copy Out做法步骤

    1. 调用该函数的时候,先复制实参的值,产生副本(通过get方法)
    2. 将副本的内存地址传入函数中(副本进行引用传递),在函数内部可以修改副本的值
    3. 函数返回之后,将副本的值覆盖到实参的值(通过set方法)

    之所以使用copy in copy out的方法实现是为了保证属性观察器的功能单一原则

func loadServerData(url: String, parameter: inout Dictionary<String, Any>) {
        parameter["data"] = ["name":"lcc", "school":"beautiful city"]
        print(parameter)
    }
  • 属性类型

    • 实例属性(Instance Property): 只能通过实例去访问

      1. 存储属性: 存储在实例内存中,每个实例都有一份
      2. 计算实例属性
    • 类型属性:只能通过类型去访问

      1. 存储类型属性:整个程序运行过程中,就只有一份内存(类似于全局变量)
      2. 计算属性
    • 类型属性的细节

      1. 不同与存储实例属性,必须给储存属性类型设置初始值,原因是因为类型属性没有像实例属性那样的init的初始化器构造方法来初始化存储属性
      2. 存储类型属性类型默认就是lazy,会在第一次使用的时候才初始化,即便是多线程同时访问,也能够保证其只初始化一次,所以存储类型属性实线程安全的属性
      3. 存储类型属性可以是let
      4. 枚举类型也可以定义为类型属性(存储类型属性、计算类型属性)
        可以通过static定义类型属性
        如果实类,也可以用关键字class定义类型计算属性
  • classstatic的区别

    相同点'

    1. 可以修饰方法,static 修饰的方法叫做静态方法,class修饰的叫做类方法。
    2. 都可以修饰计算属性。

    不同点

    1. class 不能修饰存储属性。
    2. class 修饰的计算属性可以被重写,static 修饰的不能被重写。
    3. static 可以修饰存储属性,static 修饰的存储属性称为静态变量(常量)。
    4. static 修饰的静态方法不能被重写,class 修饰的类方法可以被重写。
    5. class 修饰的类方法被重写时,可以使用static 让方法变为静态方法。
    6. class 修饰的计算属性被重写时,可以使用static 让其变为静态属性,但它的子类就不能被重写了。
    7. class 只能在类中使用,但是static 可以在类,结构体,或者枚举中使用。
    8. Swiftclassstructenum都是可以实现protocol的.
    9. 如果在protocol里定义一个类型域上的方法或者计算属性的话使用class进行定义,但是在实现时还是按照规则:在class里使用class关键字,而在structenum中仍然使用static
func testTypePerperty() {
        struct Car {
            static var count: Int = 0
            init() {
                Car.count += 1
            }
        }
                
        class Person{
            class var name: String {
                get{
                    return "leo"
                }
            }
            required init() {}
        }
        
        func useCarDemo() {
            let c1 = Car()
            let c2 = Car()
            let c3 = Car()
            print(Car.count)
            print(Person.name)
        }
        
        useCarDemo()
    }
  • 单例模式
    可以通过static定义常量的方式来实现单例,这里需要说明一下,因为Swift中静态常量是放在全局变量区的,全局变量的实例又是线程安全且为lazy加载模式,所以此时不用像oc中使用dispach_once来进行实例,也保证了其内部的原子性。
class FileManager {
        static let shareInstance = FileManager()
        required init() {}
    }
    
    class FileManagerCustom {
        
        var path: String = ""
        var name: String = ""
        
        //如果要定制化一些实例属性内容,可以用大括号将一些设置属性包起来然后实例返回
        static let shareInstance = { () -> FileManagerCustom in
            var fileManagerCustom = FileManagerCustom()
            fileManagerCustom.path = "~/DeskTop/xxxx.md"
            fileManagerCustom.name = "xxxx.md"
            return fileManagerCustom
        }()
        
        func printLoaclPath() {
            print(self.path,self.name)
        }
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容