swift

《Swift从入门到精通》(二十一):自动引用计数(ARC)初识

2021-10-19  本文已影响0人  萧1帅

自动引用计数(ARC)(学习笔记)

环境Xcode 11.0 beta4 swift 5.1

引用关系图一 引用关系图二 引用关系图三 引用关系图四
```swift
john = nil
// Prints "John Appleseed is being deinitialized"

![引用关系图五](https://img.haomeiwen.com/i6935167/0fe67d26ac615caa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

unit4A = nil
// Prints "Apartment 4A is being deinitialized"

![引用关系图六](https://img.haomeiwen.com/i6935167/e78f07bfa6551d48.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

*   `unowned` 引用与 `weak` 相似都不产生强引用,不同的是 `unowned` 引用总是有一个值,ARC不会自动将其置 nil,这意味着定义时要使用非可选类型
*   只有在确认引用一起在引用实例没有被释放,才能使用 `unowned` 引用
*   如果试图在 `unowned` 实例被释放后访问,将会产生运行时错误
*   以下是 `Customer` `CreditCard` 示例,`Customer`可以没有 `CreditCard` ,但  `CreditCard` 一定属于一个 `Customer`

    ```
    class Customer {
        let name: String
        var card: CreditCard?
        init(name: String) {
            self.name = name
        }
        deinit { print("\(name) is being deinitialized") }
    }
    //
    class CreditCard {
        let number: UInt64
        unowned let customer: Customer
        init(number: UInt64, 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!)
    // 引用关系如下

    ```

![引用关系图七](https://img.haomeiwen.com/i6935167/43fbd2266401a2f6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

john = nil
// 引用关系如下

![引用关系图八](https://img.haomeiwen.com/i6935167/52612520fd5bd442.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

* 上面的 unowned 示例是安全的引用,如果你要禁止运行时安全检查(例如出于性能考虑)可以用 unowned(unsafe) ,那这样开发者有责任检查代码的安全性;
如果要访问已经销毁的 unsafe unowned 引用时,将会访问之前的内存位置,是不安全的操作


*   `unowned` 引用与隐式解包属性
*   上面的两种解决方法基本包含大部分的情况,但还有第三种情况,就是属性都不能为nil,此时就要结合使用 `unowned` 引用与隐式解包

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)")
// Prints "Canada's capital city is called Ottawa"
// 这里有几点说明
// 1\. City 的初始化是在 Country 初始化里,按照前面所说的两阶段初始化是不行,因为 City 初始化里用到 Country,此处的解决办法是隐式解包
// 这意味着属性 capitalCity 有默认初始值 nil, 只是访问时不需要解包
// 2\. 因此 Country 的初始化在 name 赋值完成时就初始化完毕,后面才可以将 self 作为参数传入

*   **闭包的强引用**
    *   示例

        ```
        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")
            }
        }
        let heading = HTMLElement(name: "h1")
        let defaultText = "some default text"
        heading.asHTML = {
            return "<\(heading.name)>\(heading.text ?? defaultText)</\(heading.name)>"
        }
        print(heading.asHTML())
        // Prints "<h1>some default text</h1>"
        var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
        print(paragraph!.asHTML())
        // Prints "<p>hello, world</p>"
        // 创建一个新实例,引用 示意图如下
        paragraph = nil 
        // 此时实例并不会被销毁

        ```

![引用关系图九](https://img.haomeiwen.com/i6935167/e856a347d5186f57.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

*   **解决闭包的强引用**
    *   定义一个捕获列表,写在参数列表和返回值之前

        ```
        lazy var someClosure = {
            [unowned self, weak delegate = self.delegate]
            (index: Int, stringToProcess: String) -> String in
            // closure body goes here
        }
        // 如果没有参数列表,没有返回值
        lazy var someClosure = {
            [unowned self, weak delegate = self.delegate] in
            // closure body goes here
        }

        ```

    *   `unowned` `weak` 引用,两者的区别如上面所说的一样

        ```
        class HTMLElement {
            let name: String
            let text: String?
            lazy var asHTML: () -> String = {
                [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())
        // Prints "<p>hello, world</p>"
        // 创建一个新实例,引用 示意图如下
        paragraph = nil
        // Prints "p is being deinitialized"

        ```



上一篇 下一篇

猜你喜欢

热点阅读