JHHK

欢迎来到我的个人网站
行者常至 为者常成

15_02、访问控制、内存管理(二)

目录

访问控制

//一、内存管理
func memoryManager(){
    
    //一、引用问题
    do{
        print("----------1-------")
        /**
         跟OC一样,Swift也是采取基于引用计数的ARC内存管理方案(针对堆空间)
         
         Swift的ARC中有3种引用:
         
         1、强引用(strong reference):默认情况下,引用都是强引用
         
         2、弱引用(weak reference):通过weak定义弱引用
         必须是可选类型的var,因为实例销毁后,ARC会自动将弱引用设置为nil
         ARC自动给弱引用设置nil时,不会触发属性观察器
         
         3、无主引用(unowned reference):通过unowned定义无主引用
         不会产生强引用,非可选类型,实例销毁后仍然存储着实例的内存地址(类似于OC中的unsafe_unretained)
         试图在实例销毁后访问无主引用,会产生运行时错误(野指针)
         Fatal error: Attempted to read an unowned reference but object 0x0 was already deallocated
         
         闭包的循环引用问题:类似block的循环引用
         */
        
        /**
         weak、unowned的使用限制
         weak、unowned只能用在类实例上面
         */
        class Person {}
        weak    var p0: Person?
        weak    var p1: AnyObject?
        unowned let p2: Person?
        unowned let p3: AnyObject?
        
        do{
            p0 = Person()
            p1 = Person()
            p2 = Person()
            p3 = Person()
        }
        
        /**
         (lldb) po p0
         nil

         (lldb) po p1
         nil

         (lldb) po p2
         Optional<Person>
           - some : <Person: 0x10072f820>

         (lldb) po p3
         Optional<AnyObject>
           - some : <Person: 0x10072dce0>
         */
    }
    
    
    //二、自动释放池
    do{
        print("----------2-------")
        struct Person {
            var age:Int = 18
            var name:String = ""
            func run() -> Void {
                print("run")
            }
        }
        
       autoreleasepool {
            let p = Person(age: 20, name: "Jack")
            p.run()
        }
    }
    
    //三、循环引用
    do{
        print("-------3-------")
        /**
         weak、unowned 都能解决循环引用的问题,unowned 要比 weak 少一些性能消耗
         在生命周期中可能会变为 nil 的使用 weak
         初始化赋值后再也不会变为 nil 的使用 unowned
         */
    }
    
    //4、闭包的循环引用
    do{
        print("-------4-------")
        
        /**
         闭包表达式默认会对用到的外层对象产生额外的强引用(对外层对象进行了retain操作)
         下面代码会产生循环引用,导致Person对象无法释放(看不到Person的deinit被调用)
         */
        class Person {
            var fn: (() -> ())?
            func run() { print("run") }
            deinit { print("deinit") }
        }
        
        print("------4-0-------")
        func test() {
            let p = Person()
            //p对象强引用闭包表达式,闭包表达式对p也产生强引用
            p.fn = { p.run() }
            p.fn?()
        }
        test()
        
        print("------4-1-------")
        
        //在闭包表达式的捕获列表声明weak或unowned引用,解决循环引用问题
        func test1(){
            let p  = Person()
            print(Mems.ptr(ofRef: p))
            p.fn = {
                [unowned p] in
                print(Mems.ptr(ofRef: p))
                p.run()
            }
            p.fn?()
        }
        test1()
        
        print("------4-2-------")
        
        func test2 () {
            let p = Person()
            p.fn = {
                [weak p] in
                p?.run()
            }
        }
        test2()
        
        print("------4-3-------")
        
        func test3 () {
            let p = Person()
            p.fn = {
                [weak wp = p, unowned up = p, a = 10 + 20] in
                wp?.run()
            }
        }
        test3()
    }
    
    //5、闭包的循环引用2
    do{
        //如果想在定义闭包属性的同时引用self,这个闭包必须是lazy的(因为在实例初始化完毕之后才能引用self)
        class Person {
            
            //闭包fn内部如果用到了实例成员(属性、方法)
            //编译器会强制要求明确写出self
            lazy var fn: (() -> ()) = {
                [weak self] in
                self?.run()
            }
            func run() { print("run")
                
            }
            deinit { print("deinit") }
        }
        
        //如果lazy属性是闭包调用的结果,那么不用考虑循环引用的问题(因为闭包调用后,闭包的生命周期就结束了)
        class Person2 {
            var age: Int = 0
            lazy var getAge: Int = {
                self.age
            }()
            deinit { print("deinit") }
        }
    }
}

逃逸闭包

//二、逃逸闭包
func escapingClosure() -> Void {
    
    //1、逃逸闭包
    do{
        print("--------1--------")
        /**
         非逃逸闭包、逃逸闭包,一般都是当做参数传递给函数
            非逃逸闭包:闭包调用发生在函数结束前,闭包调用在函数作用域内
            逃逸闭包:闭包有可能在函数结束后调用,闭包调用逃离了函数的作用域,需要通过@escaping声明
         */
        
        typealias Fn = () -> ()
        
        // fn是非逃逸闭包
        func test1(_ fn: Fn) { fn() }
        
        // fn是逃逸闭包
        var gFn: Fn?
        func test2(_ fn: @escaping Fn) { gFn = fn }
        
        // fn是逃逸闭包
        func test3(_ fn: @escaping Fn) {
            DispatchQueue.global().async {
                fn()
            }
        }
        
        
        class Person {
            var fn: Fn
            
            // fn是逃逸闭包
            init(fn: @escaping Fn) {
                self.fn = fn
            }
            
            func run() {
                // DispatchQueue.global().async也是一个逃逸闭包
                // 它用到了实例成员(属性、方法),编译器会强制要求明确写出self
                DispatchQueue.global().async {
                    self.fn()
                }
            }
        }
    }

    
    //2、逃逸闭包的注意点
    do{
        print("-------2-------")
        
        //逃逸闭包不可以捕获inout参数
        
        typealias Fn = () -> ()
        func other1(_ fn: Fn) { fn() }
        func other2(_ fn: @escaping Fn) { fn() }
        func test(value: inout Int) -> Fn {
            other1 { value += 1 }
                
            // error: 逃逸闭包不能捕获inout参数
            //other2 { value += 1 }
                
            func plus() { value += 1 }
            
            func plus1(){}
                
            // error: 逃逸闭包不能捕获inout参数
            //return plus
            
            return plus1
        }
    }
}

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.