美文网首页
Swift4 基础部分: Automatic Reference

Swift4 基础部分: Automatic Reference

作者: Arnold134777 | 来源:发表于2017-09-14 08:07 被阅读87次

    本文是学习《The Swift Programming Language》整理的相关随笔,基本的语法不作介绍,主要介绍Swift中的一些特性或者与OC差异点。

    系列文章:

    Swift uses Automatic Reference Counting (ARC) to track and 
    manage your app’s memory usage. In most cases, this means 
    that memory management “just works” in Swift, and you do 
    not need to think about memory management yourself. ARC 
    automatically frees up the memory used by class instances 
    when those instances are no longer needed.
    
    • Swift中是引用自动引用计数来处理app中的内存占用。也就是说你不需要去考虑app中的内存的管理。当类的实例不再被使用时,ARC会自动释放其占用的内存。
    Reference counting only applies to instances of classes. 
    Structures and enumerations are value types, not reference 
    types, and are not stored and passed by reference.
    
    • 引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。

    ARC的工作机制(How ARC Works)

    Every time you create a new instance of a class, ARC 
    allocates a chunk of memory to store information about 
    that instance. This memory holds information about the 
    type of the instance, together with the values of any 
    stored properties associated with that instance. 
    
    Additionally, when an instance is no longer needed, ARC 
    frees up the memory used by that instance so that the 
    memory can be used for other purposes instead. This 
    ensures that class instances do not take up space in 
    memory when they are no longer needed.
    
    • 当你每次创建一个类的新的实例的时候,ARC 会分配一大块内存用来储存实例的信息。内存中会包含实例的类型信息,以及这个实例所有相关属性的值。此外,当实例不再被使用时,ARC会释放实例所占用的内存,并让释放的内存其他使用。这就确保了不再被使用的实例,不会一直占用内存空间。

    ARC的实践(ARC in Action)

    例子:

    class Person {
        let name:String;
        init(name: String){
            self.name = name;
            print("\(name) is being initialized");
        }
        
        deinit {
            print("\(name) is being deinitialized");
        }
    }
    
    var reference1: Person? = Person(name:"xz");
    var reference2: Person? = reference1;
    var reference3: Person? = reference1;
    
    reference1 = nil;
    reference2 = nil;
    reference3 = nil;
    

    执行结果:

    xz is being initialized
    xz is being deinitialized
    

    类实例之间的循环强引用(Strong Reference Cycles Between Class Instances)

    循环引用在OC中也是常见的,直接看一个例子:

    
    class Person {
        let name: String;
        init(name: String) { self.name = name; }
        var apartment: Apartment?;
        deinit { print("\(name) is being deinitialized"); }
    }
    
    class Apartment {
        let unit: String;
        init(unit: String) { self.unit = unit; }
        var tenant: Person?;
        deinit { print("Apartment \(unit) is being deinitialized"); }
    }
    
    var john: Person?;
    var unit4A: Apartment?;
    
    john = Person(name: "John Appleseed");
    unit4A = Apartment(unit: "4A");
    
    // 以下是核心引发的例子
    john!.apartment = unit4A;
    unit4A!.tenant = john;
    
    john = nil;
    unit4A = nil;
    

    解决类实例之间的强循环引用(Resolving Strong Reference Cycles Between Class Instances)

    Swift provides two ways to resolve strong reference cycles 
    when you work with properties of class type: weak 
    references and unowned references.
    
    • Swift 提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)。

    弱引用(Weak References)

    A weak reference is a reference that does not keep a 
    strong hold on the instance it refers to, and so does not 
    stop ARC from disposing of the referenced instance. This 
    behavior prevents the reference from becoming part of a 
    strong reference cycle. You indicate a weak reference by 
    placing the weak keyword before a property or variable 
    declaration.
    
    • 弱引用不会牢牢保持住引用的实例,并且不会阻止 ARC 销毁被引用的实例。这种行为阻止了引用变为循环强引用。声明属性或者变量时,在前面加上weak关键字表明这是一个弱引用。

    直接改写一下上述的例子:

    class Apartment {
        let unit: String;
        init(unit: String) { self.unit = unit; }
        weak var tenant: Person?; // 注意此处
        deinit { print("Apartment \(unit) is being deinitialized"); }
    }
    

    执行结果:

    John Appleseed is being deinitialized
    Apartment 4A is being deinitialized
    

    无主引用(Unowned References)

    Like a weak reference, an unowned reference does not keep 
    a strong hold on the instance it refers to. Unlike a weak 
    reference, however, an unowned reference is used when the 
    other instance has the same lifetime or a longer lifetime. 
    You indicate an unowned reference by placing the unowned 
    keyword before a property or variable declaration.
    
    • 和弱引用类似,无主引用不会牢牢保持住引用的实例。和弱引用不同的是,无主引用拥有同样或者更长的生命周期相对其他的实例。
    An unowned reference is expected to always have a value. 
    As a result, ARC never sets an unowned reference’s value 
    to nil, which means that unowned references are defined 
    using nonoptional types.
    
    • 无主引用一直都是有值的,ARC无法在实例被销毁后将无主引用设为nil,也就是说无主引用是非可选型的。

    例子:

    class Customer {
        let name: String;
        var card: CreditCard?;
        init(name: String) {
            self.name = name;
        }
        deinit { print("\(name) is being deinitialized"); }
    }
    
    class CreditCard {
        let number: Int;
        unowned let customer: Customer;
        init(number: Int, customer: Customer) {
            self.number = number;
            self.customer = customer;
        }
        deinit { print("Card #\(number) is being deinitialized"); }
    }
    
    var john: Customer?;
    john = Customer(name: "John Appleseed");
    john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!);
    john = nil;
    

    执行结果:

    John Appleseed is being deinitialized
    Card #1234567890123456 is being deinitialized
    

    无主引用以及隐式解析可选属性(Unowned References and Implicitly Unwrapped Optional Properties)

    当相互引用的属性都不允许为nil时,此时就需要使用无主引用+隐式解析可选属性。

    例子:

    class Country {
        let name: String;
        var capitalCity: City!;
        init(name: String, capitalName: String) {
            self.name = name;
            self.capitalCity = City(name: capitalName, country: self);
        }
    }
    
    class City {
        let name: String;
        unowned let country: Country;
        init(name: String, country: Country) {
            self.name = name;
            self.country = country;
        }
    }
    
    var country = Country(name: "Canada", capitalName: "Ottawa");
    print("\(country.name)'s capital city is called \(country.capitalCity.name)");
    

    执行结果:

    Canada's capital city is called Ottawa
    

    闭包中的循环强引用(Strong Reference Cycles for Closures)

    与OC中的block引起的循环强引用一致,直接看一下例子:

    class HTMLElement {
        
        let name: String
        let text: String?
        
        lazy var asHTML: () -> String = {
            if let text = self.text {
                return "<\(self.name)>\(text)</\(self.name)>"
            } else {
                return "<\(self.name) />"
            }
        }
        
        init(name: String, text: String? = nil) {
            self.name = name
            self.text = text
        }
        
        deinit {
            print("\(name) is being deinitialized")
        }
    }
    
    var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world");
    print(paragraph!.asHTML());
    paragraph = nil;
    

    执行结果:

    <p>hello, world</p>
    

    疑问:

    • 为什么deinit函数没有执行? 因为asHTML的闭包"捕获"了self,同时asHTML的属性持有了闭包的强引用。二者之间产生了循环强引用。

    解决闭包引起的循环强引用(Resolving Strong Reference Cycles for Closures)

    You resolve a strong reference cycle between a closure and a 
    class instance by defining a capture list as part of the 
    closure’s definition.
    
    • 为解决闭包和类实例之间的循环强引用,可以定义闭包时同时定义捕获列表作为闭包的一部分。

    具体捕获列表的语法参考如下:

    lazy var someClosure: (Int, String) -> String = {
        [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
        // closure body goes here
    }
    
    Define a capture in a closure as an unowned reference when the 
    closure and the instance it captures will always refer to each 
    other, and will always be deallocated at the same time.
    
    Conversely, define a capture as a weak reference when the 
    captured reference may become nil at some point in the future.
    
    • 将闭包内的捕获定义为无主引用,当闭包和捕获的实例总是互相引用时并且总是同时销毁时。相反的,将闭包内的捕获定义为弱引用,当捕获引用有时可能会是nil时。

    上述的例子中,显然无主引用可以解决上述闭包引起的循环强引用问题,具体代码如下:

    class HTMLElement {
        
        let name: String
        let text: String?
        
        lazy var asHTML: () -> String = {
             [unowned self] () ->String in // 可简写为[unowned self] in
            if let text = self.text {
                return "<\(self.name)>\(text)</\(self.name)>"
            } else {
                return "<\(self.name) />"
            }
        }
        
        init(name: String, text: String? = nil) {
            self.name = name
            self.text = text
        }
        
        deinit {
            print("\(name) is being deinitialized")
        }
    }
    
    var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world");
    print(paragraph!.asHTML());
    paragraph = nil;
    

    执行结果:

    <p>hello, world</p>
    p is being deinitialized
    

    相关文章

      网友评论

          本文标题:Swift4 基础部分: Automatic Reference

          本文链接:https://www.haomeiwen.com/subject/bgyrsxtx.html