บทช่วยสอน สวิฟท์นับการอ้างอิงอัตโนมัติ (ARC) ที่ดีที่สุดในปี 2024 ในบทช่วยสอนนี้ คุณสามารถเรียนรู้ ตัวอย่างของการไหลเวียนที่แข็งแกร่งระหว่างการอ้างอิงคลาส,ปิดห่วงเกิดจากการอ้างอิงที่แข็งแกร่ง,ลำดับที่อ่อนแอและการอ้างอิง ownerless,
สวิฟท์ใช้อ้างอิงอัตโนมัตินับ (ARC) กลไกนี้เพื่อติดตามและจัดการหน่วยความจำ
ภายใต้สถานการณ์ปกติเราไม่จำเป็นที่จะปล่อยตนเองหน่วยความจำเพราะ ARC จะเป็นตัวอย่างของการเรียนจะไม่ได้ใช้โดยอัตโนมัติเพิ่มหน่วยความจำ
แต่เรายังคงต้องใช้รหัสจัดการหน่วยความจำในบางกรณี
เมื่อ init () สร้างตัวอย่างใหม่ของชั้นเรียนของแต่ละวิธีเมื่อ 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 อินสแตนซ์ เพื่อให้ทั้งสองวัตถุอ้างอิงผลิตที่แข็งแกร่งวงกลม
ปิดห่วงที่เกิดจากการอ้างแก้ปัญหาที่แข็งแกร่ง: เมื่อคุณกำหนดปิดในเวลาเดียวกันเป็นส่วนหนึ่งของความหมายของการปิดการจับรายชื่อในลักษณะนี้จะสามารถแก้ไขได้ระหว่างปิดห่วงและการอ้างอิงที่แข็งแกร่งให้กับอินสแตนซ์ชั้นเรียน
เมื่อเป็นตัวอย่างของการปิดและถูกจับมักจะอ้างถึงกันและกันและอยู่เสมอในเวลาเดียวกันที่ทำลายจับภายในความหมายของการปิดคือไม่มีการอ้างอิงหลัก
ตรงกันข้ามเมื่ออ้างอิงจับบางครั้งอาจจะไม่มีการปิดจะถูกจับภายในความหมายของการอ้างอิงที่อ่อนแอ
หากคุณสามารถจับภาพการอ้างอิงจะไม่ถูกตั้งค่าเป็นศูนย์ก็ควรจะไม่มีการอ้างอิงหลักแทนการอ้างอิงที่อ่อนแอ
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 被析构