Swift中的访问控制与内存管理

2021-02-20  本文已影响0人  a_只羊

自定义Description内容打印

关于 Self 的使用

关于断言 assert

//条件满足true的时候才会继续执行,否则中断进程,打印错误
assert(fasle, "断言错误")

关于 fatalError 错误

访问控制

class Person{}
public typealias MuyPerson = Person //该语句会报错,原因是public访问级别低于class Person 的默认访问级别 internal

注:

  1. 父类的访问权限要小于子类
  2. 直接在全局作用域下定义的 private 等价于 fileprivate
public class PublicClass {
    public var p1 = 0 
    var p2 = 0 //internal
    fileprivatre func f1() {} //fileprivate
    private func f2() {} //private
}


class InternalClass { // internal
    var p = 0 // internal 
    fileprivate func f1() {} //fileprivate
    private func f2 () {} //private
}

class Test {
    private class Person {} //private所作用的范围是外面的大括号
    fileprivate class Student : Person {} //会报错,因为子类Student的访问权限低于父类
}

private class Person {} //private所作用的范围是整个文件,由于文件声明在全局区域,所以private作用域等同于fileprivate
fileprivate class Student : Person {} //不会报错,此时两者的权限是一致的

/*
报错原因:
1. Dog中的成员变量是由private修饰,所以在其所在的作用域内是能够访问的,由于Person中walk方法在Dog成员变量的作用域外,所以不能够访问其成员变量以及方法
2. 由于 Dog 定义在全局区,即使使用关键字 private 修饰,也等同于 fileprivate 修饰,所以 Person 类与其成员变量 Dog 类型访问权限一致,所以能够正常实例

*/
private struct Dog {
    private var age: Int = 0
    private func run() {}
}

fileprivate struct Person {
    var dog: Dog = Dog()
    mutating func walk() {
    dog.run() // 会报访问错误
    dog.age = 1 // 会报访问错误
}
}

初始化器

如果一个public类想要在另一个模块调用编译生成的默认无参初始化器,必须显示提供public的无参初始化器,因为public类的默认初始化器是 internal 访问级别

注意:

  1. required 初始化器必须跟它所属的类拥有相同的访问级别
  2. 如果结构体有 private/fileprivate 的存储实例属性,那么它的成员初始化器也是 private/fileprivate,否则默认就是 internal

枚举类型的 case 访问权限

协议的访问权限

注意:
public 修饰的类型/结构体,成员变量以及方法的默认类型是 internal 类型,权限比public权限小,所以此时遵守的协议的权限是 public ,所以会报错

public protocol Runnable {
    func run()
}

public class Person : Runable {
    internal func run {}
}

扩展的访问权限

public class Person {
    private func run0() {}
    private func eat0() {
        run1()
    }
}

extension Person {
    private func run1() {}
    private func eat1() {
        run0()
    }
}

extension Person {
    private func eat2() {
        run1()
    }
}

有时候需要将某些方法暂存到变量中,等到合适的实际动态去调用它,可以参照如下的方法:

  1. 将需要调用的方法取出来并存到变量中
  2. 实例方法所在的类并传入到第一步的方法中,并存储到变量中
  3. 通过第二步存储的方法进行相关调用
struct Person {
    var age: Int
    func run(_ v: Int) { print("func run"), age, v }
}

/*
调用步骤
*/
var fn1 = Person.run
var fn2 = fn1(Person(age: 10))
fn2(20)

当存在两个重名的类型方法与实例方法的时候,在取值的时候,设定取值结果的类型来确定当前的静态方法或者实例方法

struct Person {
    var age: Int
    func run(_ v: Int) { print("func run", age, v) }
    static func run(_ v: Int) { print("static func run", v) }
}

//取实例方法
var fn: (Person) -> (Int) -> () = Person.run
fn(Person(age: 20))(20)

//取静态方法
var fn1: (Int)->() = Person.run
fn1(20)

内存管理

闭包表达式默认会对用到的外层对象产生额外的强引用(对外层对象进行 retain 操作)


/*
案例一
*/
class Person {
    var fn: (()->())?
    func run() { print("run") }
    deinit { print("deinit") }
}

func test() {
    let p = Person()
    
    //会导致循环引用问题
    p.fn = { p.run() }
    
    //解决方法1:weak
    p.fn = {[weak p] in
        p?.run()
    }
    
    //解决方法2:unowned
    p.fn = {[unowned p] in
        p?.run()
    }
    
    //声明多个弱引用
    p.fn = {[weak wp = p, unowned up = p, a = 10 + 20] in
        wp?.run()
    }
}


/*
案例二:闭包中引用self, self中引用闭包
*/
class Person1 {
    //使用weak去除循环引用问题
    lazy var fn: (()->()) = {[weak p = self] in 
        p?.run()
    }
    func run() { print("run") }
    deinit { print("deinit") }
}

/*
案例三:闭包中引用self之后直接实例运行,不差生循环引用
以下不会产生引用循环:
闭包声明之后立即调用,调用完成之后,闭包销毁,返回闭包结果,此时getAge不会对闭包产生引用,引用的是闭包返回的值结果,所以此种情况下不会出现循环引用问题
*/
class Person2 {
    var age: Int = 0
    //使用weak去除循环引用问题
    lazy var getAge: Int = {
        self.age
    }()
    func run() { print("run") }
    deinit { print("deinit") }
}

逃逸闭包 @escaping

typealias Fn = ()->()

var gFn: Fn?
//非逃逸闭包
func test(_ fn: Fn) { fn() }

//fn逃逸闭包
func test2(_ fn: @escaping Fn) { gFn = fn }

//fn放在dispatch中同样是逃逸闭包
func test3(_ fn: @escaping Fn) { 
    DispatchQueue.global().async {
        fn()
    }
}

func run(){
    //这一块可以根据具体业务来定,如果想要在异步线程执行完成之后强制执行当前dispatch所在区域方法,
    //那么这里直接引用self确保当前作用域范围内能够执行完成并不会立即销毁,可以保证在执行完成dispatch之后销毁内容达到延迟销毁的目的
    //如果需要立即中断,则可以使用弱引用的方式来完成
    DispatchQueue.global().async {[weak p = self] in 
        p?.fn()
    }
}

上一篇 下一篇

猜你喜欢

热点阅读