YouTip LogoYouTip

Swift Arc

Swift Automatic Reference Counting (ARC) \\n\\nSwift uses the Automatic Reference Counting (ARC) mechanism to track and manage your app's memory usage.\\n\\nTypically, we do not need to manually release memory, because ARC automatically frees the memory taken up by a class instance when it is no longer in use.\\n\\nHowever, in some cases, we still need to implement memory management in our code.\\n\\n### ARC Functionality\\n\\n* Each time you create a new instance of a class using the init() method, ARC allocates a chunk of memory to store the instance's information.\\n\\n* The memory contains the instance's type information, along with the values of all its related properties.\\n\\n* When an instance is no longer needed, ARC frees the memory occupied by the instance so that the freed memory can be used for other purposes.\\n\\n* To ensure that an instance in use is not destroyed, ARC tracks and counts how many properties, constants, and variables are currently referencing each instance.\\n\\n* When an instance is assigned to a property, constant, or variable, they all create a strong reference to that instance. As long as the strong reference remains, the instance is not allowed to be destroyed.\\n\\n### ARC Example\\n\\nclass Person { let name: String init(name: String) { self.name = name print("(name) Start initializing") } deinit { print("(name) is destructed") }}// The value is automatically initialized to nil, and currently does not refer to an instance of Person class var reference1: Person?var reference2: Person?var reference3: Person?// Create a new instance of Person class reference1 = Person(name: "Tutorial")//Assign to other two variables, the instance will have two more strong references reference2 = reference1 reference3 = reference1 //Break the first strong reference reference1 = nil//Break the second strong reference reference2 = nil//Break the third strong reference, and call the destructor reference3 = nil\\nThe output of the above program execution is:\\n\\nTutorial Start initializingTutorial is destructed\\n\\n* * *\\n\\n## Strong Reference Cycles Between Class Instances\\n\\nIn the example above, ARC tracks the number of references to the newly created Person instance and destroys the Person instance when it is no longer needed.\\n\\nHowever, we might write code where a class instance never has zero strong references. This happens when two class instances hold a strong reference to each other, preventing each other from being destroyed. This is known as a strong reference cycle.\\n\\n### Example\\n\\nThe following shows an example of an inadvertently created strong reference cycle. The example defines two classes: Person and Apartment, modeling an apartment and its residents:\\n\\nclass Person { let name: String init(name: String) { self.name = name } var apartment: Apartment? deinit { print("(name) is destructed") }}class Apartment { let number: Int init(number: Int) { self.number = number } var tenant: Person? deinit { print("Apartment #(number) is destructed") }}// Both variables are initialized to nil var tutorial: Person?var number73: Apartment?// Assign tutorial = Person(name: "Tutorial") number73 = Apartment(number: 73)// The exclamation mark is used to unwrap and access instances in optional variables tutorial and number73// Circular strong reference is created tutorial!.apartment = number73 number73!.tenant = tutorial // When breaking the strong references held by the tutorial and number73 variables, the reference count does not drop to 0, and the instance is not deallocated by ARC. // Note, when you set these two variables to nil, none of the destructors are called.// Strong reference cycles prevent the deallocation of Person and Apartment class instances, causing memory leaks in your application. tutorial = nil number73 = nil\\n### Resolving Strong Reference Cycles Between Instances\\n\\nSwift provides two ways to resolve strong reference cycles you encounter when working with class properties:\\n\\n* Weak references\\n* Unowned references\\n\\nWeak and unowned references allow one instance in a reference cycle to refer to another instance without keeping a strong reference. This allows the instances to refer to each other without creating a strong reference cycle.\\n\\nUse a weak reference for instances that can become nil during their lifecycle. Conversely, use an unowned reference for instances that will never become nil after initialization.\\n\\n### Weak Reference Example\\n\\nclass Module { let name: String init(name: String) { self.name = name } var sub: SubModule? deinit { print("(name) Main module") }}class SubModule { let number: Int init(number: Int) { self.number = number } weak var topic: Module? deinit { print("Submodule topic count is (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\\nThe output of the above program execution is:\\n\\nARC Main moduleSubmodule topic count is 4\\n### Unowned Reference Example\\n\\nclass 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("The student's score is (marks)") }}var module: Student?module = Student(name: "ARC")module!.section = Marks(marks: 98, stname: module!)module = nil\\nThe output of the above program execution is:\\n\\nARC The student's score is 98\\n\\n* * *\\n\\n## Strong Reference Cycles Caused by Closures\\n\\nA strong reference cycle can also occur if you assign a closure to a property of a class instance, and the body of that closure uses the instance. The closure might access an instance property, such as self.someProperty, or call a method on the instance, such as self.someMethod. Both of these situations cause the closure to "capture" self, creating a strong reference cycle.\\n\\n### Example\\n\\nThe following example shows how a strong reference cycle is created when a closure references self. The example defines a class called HTMLElement, which provides a simple model for an individual element within HTML:\\n\\nclass HTMLElement { let name: String let text: String? lazy var asHTML: () -> String = { if let text = self.text { return "(text)" } else { return "" } } init(name: String, text: String? = nil) { self.name = name self.text = text } deinit { print("(name) is being deinitialized") } }// Create instance and print information var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")print(paragraph!.asHTML())\\nThe HTMLElement class creates a strong reference cycle between the class instance and the closure assigned to its asHTML default value.\\n\\nThe instance's asHTML property holds a strong reference to the closure. However, the closure uses self within its body (referencing self.name and self.text), so the closure captures self, which means the closure in turn holds a strong reference back to the HTMLElement instance. This creates a strong reference cycle between the two objects.\\n\\nResolving strong reference cycles caused by closures: You can resolve a strong reference cycle between a closure and a class instance by defining a capture list as part of the closure's definition.\\n\\n* * *\\n\\n## Weak and Unowned References\\n\\nWhen the closure and the captured instance always refer to each other and are always deallocated at the same time, define the capture in the closure as an unowned reference.\\n\\nConversely, when the captured reference may become nil at some point, define the capture in the closure as a weak reference.\\n\\nIf the captured reference will absolutely never become nil, you should use an unowned reference instead of a weak reference.\\n\\n### Example\\n\\nIn the previous HTMLElement example, an unowned reference is the correct way to resolve the strong reference cycle. Here is how you would write the HTMLElement class to avoid the strong reference cycle:\\n\\nclass HTMLElement { let name: String let text: String? lazy var asHTML: () -> String = { in if let text = self.text { return "(text)" } else { return "" } } init(name: String, text: String? = nil) { self.name = name self.text = text } deinit { print("(name) is destructed") } }//Create and print HTMLElement instance var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")print(paragraph!.asHTML())// HTMLElementThe instance will be destroyed, and you can see the message printed by its destructor paragraph = nil\\nThe output of the above program execution is:\\n\\n

hello, world

p is destructed
← Swift Type CastingSwift Optional Chaining β†’