Latest web development tutorials

สวิฟท์นับการอ้างอิงอัตโนมัติ (ARC)

สวิฟท์ใช้อ้างอิงอัตโนมัตินับ (ARC) กลไกนี้เพื่อติดตามและจัดการหน่วยความจำ

ภายใต้สถานการณ์ปกติเราไม่จำเป็นที่จะปล่อยตนเองหน่วยความจำเพราะ ARC จะเป็นตัวอย่างของการเรียนจะไม่ได้ใช้โดยอัตโนมัติเพิ่มหน่วยความจำ

แต่เรายังคงต้องใช้รหัสจัดการหน่วยความจำในบางกรณี

ฟังก์ชั่น ARC

  • เมื่อ init () สร้างตัวอย่างใหม่ของชั้นเรียนของแต่ละวิธีเมื่อ ARC จะจัดสรรก้อนของหน่วยความจำสำหรับการจัดเก็บข้อมูลเช่น

  • ประเภทของข้อมูลที่มีอยู่ในหน่วยความจำจะเป็นกรณีเช่นเดียวกับมูลค่าของกรณีนี้คุณลักษณะทั้งหมดที่เกี่ยวข้อง

  • เมื่ออินสแตนซ์จะไม่ถูกนำมาใช้ ARC ปล่อยหน่วยความจำที่ถูกครอบครองโดยอินสแตนซ์และให้หน่วยความจำอิสระสามารถนำมาใช้เพื่อวัตถุประสงค์อื่น ๆ

  • เพื่อให้มั่นใจว่าอินสแตนซ์จะไม่ถูกทำลาย, ARC จะติดตามและคำนวณแต่ละกรณีจะถูกอ้างอิงโดยจำนวนของคุณลักษณะค่าคงที่และตัวแปร

  • ตัวอย่างที่ได้รับมอบหมายไปยังสถานที่คงที่หรือตัวแปรที่พวกเขาจะสร้างการอ้างอิงที่แข็งแกร่งเช่นนี้ตราบใดที่อ้างอิงยังคงแข็งแกร่งอินสแตนซ์ไม่ได้รับอนุญาตจะถูกทำลาย

ตัวอย่าง ARC

class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) 开始初始化")
    }
    deinit {
        print("\(name) 被析构")
    }
}

// 值会被自动初始化为nil,目前还不会引用到Person类的实例
var reference1: Person?
var reference2: Person?
var reference3: Person?

// 创建Person类的新实例
reference1 = Person(name: "w3big")


//赋值给其他两个变量,该实例又会多出两个强引用
reference2 = reference1
reference3 = reference1

//断开第一个强引用
reference1 = nil
//断开第二个强引用
reference2 = nil
//断开第三个强引用,并调用析构函数
reference3 = nil

เอาท์พุทการทำงานของโปรแกรมข้างต้นเป็น:

w3big 开始初始化
w3big 被析构

ตัวอย่างของการไหลเวียนที่แข็งแกร่งระหว่างการอ้างอิงคลาส

ในตัวอย่างข้างต้น, ARC จะติดตามจำนวนการอ้างอิงของคุณที่สร้างขึ้นใหม่คนกรณีและมันจะถูกทำลายเมื่อตัวอย่างบุคคลที่ไม่มีความจำเป็น

แต่เราอาจจะเขียนโค้ดเช่นนี้จะไม่มีชั้น 0 อ้างอิงที่แข็งแกร่ง นี้เกิดขึ้นในสองกรณีที่จะรักษาระดับการอ้างอิงที่แข็งแกร่งกับแต่ละอื่น ๆ และปล่อยให้บุคคลอื่นจะไม่ถูกทำลาย นี่คือสิ่งที่เรียกว่าอ้างอิงแบบวงกลมที่แข็งแกร่ง

ตัวอย่าง

ตัวอย่างต่อไปนี้แสดงให้เห็นห่วงโดยไม่ได้ตั้งใจผลิตอ้างอิงที่แข็งแกร่ง ตัวอย่างเช่นกำหนดสองชั้นเรียน: คนและพาร์ทเม้นท์ที่ใช้ในการสร้างแบบจำลองพาร์ทเมนท์และอาศัยอยู่เป็น:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) 被析构") }
}

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    var tenant: Person?
    deinit { print("Apartment #\(number) 被析构") }
}

// 两个变量都被初始化为nil
var w3big: Person?
var number73: Apartment?

// 赋值
w3big = Person(name: "w3big")
number73 = Apartment(number: 73)

// 意感叹号是用来展开和访问可选变量 w3big 和 number73 中的实例
// 循环强引用被创建
w3big!.apartment = number73
number73!.tenant = w3big

// 断开 w3big 和 number73 变量所持有的强引用时,引用计数并不会降为 0,实例也不会被 ARC 销毁
// 注意,当你把这两个变量设为nil时,没有任何一个析构函数被调用。
// 强引用循环阻止了Person和Apartment类实例的销毁,并在你的应用程序中造成了内存泄漏
w3big = nil
number73 = nil

ตัวอย่างของการแก้ปัญหาที่แข็งแกร่งไหลเวียนระหว่างการอ้างอิง

สวิฟท์มีสองวิธีในการแก้วงที่คุณใช้แอตทริบิวต์ชั้นเมื่อเจอคำถามที่แข็งแกร่งอ้างอิง:

  • ลำดับที่อ่อนแอ
  • ไม่มีการอ้างอิงหลัก

ลำดับที่อ่อนแอและไม่มีการอ้างอิงหลักช่วยให้การอ้างอิงแบบวงกลมในตัวอย่างของการอ้างอิงถึงกรณีอื่นโดยไม่ต้องรักษาอ้างอิงที่แข็งแกร่ง กรณีดังกล่าวสามารถอ้างถึงกันและกันโดยไม่สร้างวงจรการอ้างอิงที่แข็งแกร่ง

สำหรับวงจรชีวิตจะกลายเป็นศูนย์เช่นใช้อ้างอิงอ่อนแอ ในทางตรงกันข้ามหลังจากที่เริ่มต้นค่าจะไม่ได้รับมอบหมายให้ศูนย์เช่นการใช้การอ้างอิงที่ไม่ใช่หลัก

ตัวอย่างของการอ้างอิงที่อ่อนแอ

class Module {
    let name: String
    init(name: String) { self.name = name }
    var sub: SubModule?
    deinit { print("\(name) 主模块") }
}

class SubModule {
    let number: Int
    
    init(number: Int) { self.number = number }
    
    weak var topic: Module?
    
    deinit { print("子模块 topic 数为 \(number)") }
}

var toc: Module?
var list: SubModule?
toc = Module(name: "ARC")
list = SubModule(number: 4)
toc!.sub = list
list!.topic = toc

toc = nil
list = nil

เอาท์พุทการทำงานของโปรแกรมข้างต้นเป็น:

ARC 主模块
子模块 topic 数为 4

ไม่มีตัวอย่างอ้างอิงหลัก

class Student {
    let name: String
    var section: Marks?
    
    init(name: String) {
        self.name = name
    }
    
    deinit { print("\(name)") }
}
class Marks {
    let marks: Int
    unowned let stname: Student
    
    init(marks: Int, stname: Student) {
        self.marks = marks
        self.stname = stname
    }
    
    deinit { print("学生的分数为 \(marks)") }
}

var module: Student?
module = Student(name: "ARC")
module!.section = Marks(marks: 98, stname: module!)
module = nil

เอาท์พุทการทำงานของโปรแกรมข้างต้นเป็น:

ARC
学生的分数为 98

ปิดห่วงเกิดจากการอ้างอิงที่แข็งแกร่ง

วงจรการอ้างอิงที่แข็งแกร่งยังเกิดขึ้นเมื่อคุณกำหนดปิดเช่นชั้นของทรัพย์สินและร่างกายการปิดและการใช้งานของตัวอย่าง ร่างกายอาจปิดเข้าถึงกรณีคุณสมบัติเช่น self.someProperty หรือปิดวิธีการเช่นโทร self.someMethod ดังกล่าว ทั้งสองกรณีจะนำไปสู่การปิด "จับ" ตัวเองส่งผลให้ในวงจรของการอ้างอิงที่แข็งแกร่ง

ตัวอย่าง

วิธีหลังจากตัวอย่างต่อไปนี้แสดงให้คุณเห็นเมื่อมีการปิดคือการผลิตการอ้างอิงไปยังห่วงตัวเองอ้างอิงที่แข็งแกร่ง ตัวอย่างเช่นกำหนดระดับที่เรียกว่า HtmlElement แสดงออกในรูปแบบที่เรียบง่ายขององค์ประกอบ HTML เดียว:

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())

ระดับ HtmlElement ผลิตกรณีระดับวงจรและฝาปิด asHTML ค่าเริ่มต้นระหว่างการอ้างอิงที่แข็งแกร่ง

เช่นคุณสมบัติ asHTML แข็งแกร่งอ้างอิงถึงการปิด อย่างไรก็ตามการปิดปิดในการใช้ร่างกายของตนเอง (อ้าง self.name และ self.text) และดังนั้นจึงจับปิดตัวเองซึ่งหมายความว่าการปิดในการเปิดชูอ้างอิงแข็งแกร่ง HtmlElement อินสแตนซ์ เพื่อให้ทั้งสองวัตถุอ้างอิงผลิตที่แข็งแกร่งวงกลม

ปิดห่วงที่เกิดจากการอ้างแก้ปัญหาที่แข็งแกร่ง: เมื่อคุณกำหนดปิดในเวลาเดียวกันเป็นส่วนหนึ่งของความหมายของการปิดการจับรายชื่อในลักษณะนี้จะสามารถแก้ไขได้ระหว่างปิดห่วงและการอ้างอิงที่แข็งแกร่งให้กับอินสแตนซ์ชั้นเรียน


ลำดับที่อ่อนแอและการอ้างอิง ownerless

เมื่อเป็นตัวอย่างของการปิดและถูกจับมักจะอ้างถึงกันและกันและอยู่เสมอในเวลาเดียวกันที่ทำลายจับภายในความหมายของการปิดคือไม่มีการอ้างอิงหลัก

ตรงกันข้ามเมื่ออ้างอิงจับบางครั้งอาจจะไม่มีการปิดจะถูกจับภายในความหมายของการอ้างอิงที่อ่อนแอ

หากคุณสามารถจับภาพการอ้างอิงจะไม่ถูกตั้งค่าเป็นศูนย์ก็ควรจะไม่มีการอ้างอิงหลักแทนการอ้างอิงที่อ่อนแอ

ตัวอย่าง

HtmlElement ดังกล่าวข้างต้นตัวอย่างเช่นการอ้างอิงหลักคือไม่มีวิธีการที่เหมาะสมในการแก้ปัญหาการอ้างอิงที่แข็งแกร่งวงกลม ระดับการเขียน HtmlElement ดังกล่าวเพื่อหลีกเลี่ยงการอ้างอิงที่แข็งแกร่งวงกลม:

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) 被析构")
    }
    
}

//创建并打印HTMLElement实例
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())

// HTMLElement实例将会被销毁,并能看到它的析构函数打印出的消息
paragraph = nil

เอาท์พุทการทำงานของโปรแกรมข้างต้นเป็น:

<p>hello, world</p>
p 被析构