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) {
        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)
        }
    }

你可能感兴趣的:(Swift 属性类型以及单例模式)