Some systems manage Memory in an arbitrary way to protect Memory corruption and preventing apps from any abuse or unexpected memory allocation/deallocation. In some languages such as C/C++ Developer is in charge of releasing memory after allocation , in some other high level languages such as Java , or C# helped by a virtual environment Memory is managed by Garbage Collector GC, when an object has been instantiated , developers don’t need to release objects from Memory after using it, The same way designers of Swift setup a similar mechanism to protect memory , in Swift ARC (Automatic Reference Counting) is a mechanism to controls objects references in Memory, As we have seen in Swift some type like Structures are value type and some others like class instances are references ,Swift tracks any class references in Memory by counting them , when an object instance is referenced by a variable , by default this reference is Strong and meaning the object stay in Memory until the reference is broken down:
Notice:
ARC is a compiler feature , It automatically injects retain/release to your codes , previous to IOS 5 in Objective C we had to release objects manually (MRC) , in fact it was up to developer to release reference when it was not anymore required , back to era of Objective-C , NSObject has a property retainCount which is the object count in use , by using paire of retain / release the reference get increased/decreased manually.
Example , suppose we have a Contact class as following:
class Contact{
var name:String!
var number:String!
init(name:String,number:String){
self.name = name
self.number = number
}
deinit{
print(“Contact deallocated!")
}
}
//Instantiate Contact Class somewhere in codes
let contact = Contact(name: "Alan", number: "Boloorian”)
// contact is a Strong reference to Contact instance
Now try to execute the following codes
var cnt : Contact?
cnt = Contact(name: "Alan", number: "Boloorian”)
It instantiates Contact class , and the reference is assigned to cnt ,as by default this reference is Strong , It stay in Memory as long as reference is alive , now if we break this object reference as the following line :
cnt = nil
Immidiately after this line object instance is removed from memory and deinit is called , so it results in printing this line :
>>>Contact deallocated!
Sometimes for some reasons we reference an object instance from another object instance , in this case if we keep objects reference in Strong way, we probably have to face a Memory leak , ARC remove an instance object only if the number of strong references (Count) to this object reach zero , let’s suppose we have two objects , OBJ1 and OBJ2 in memory , and they have a Strong reference to each other , ARC attempts to remove OBJ1 from Memory , It checks Strong reference Count = 1 (From OBJ2) so It keep it in Memory , the something happens when ARC try to remove OBJ2 from memory , voila they are Locked! this situation called “ Strong Reference Cycles"
Example:
we create two class Contact and Address , and in each class we reference the other class like following, this example is not a real world example but it’s just a simple way to create a situation in which two class are strongly related to each other.
class Contact{
var name:String!
var number:String!
var mAddress:Address?
init(name:String,number:String){
self.name = name
self.number = number
print("Contact initialized")
}
deinit{
print("Contact deallocated!")
}
}
class Address{
var country:String!
var city:String!
var street:String!
var mContact:Contact?
init(country:String,city:String,street:String){
self.country = country
self.city = city
self.street = street
print("Address initialized")
}
deinit{
print("Address deallocated!")
}
}
The following graph shows the class relationship
Now somewhere in code we initialize them:
var contact:Contact?
var address:Address?
contact = Contact(name: "Alan", number: "Boloorian")
address = Address(country: "France", city: "Paris", street: "Voltaire")
address!.mContact = contact
contact!.mAddress = address
Now we break both instances and look at debugger consol to see if they deinitializer is called which indicates if our objects instances are properly removed from Memory
contact = nil
address = nil
by checking consol we see deinit is not called , this is because we have created a Strong Reference Cycle situation in which we keep two strong pointers from objects to each others and ARC cannot to recycle it because Reference Count didn’t drop to zero , ARC keep objects in Memory until its Strong Reference is greater or equal to One SR>=1 , notice in addition to mContact and mAddress the variable contact and address are also two Strong Reference to Class instances so total Reference Count for each class here is 2 , by adding contact = nil and address = nil , we remove two Strong Reference and Count drops to 1 and ARC keep it in Memory
Solution
To bring the solution in those situation Swift introduced weak and unowned references by declare a variable as weak or unowned , we tell compiler to not consider them as a Strong Reference , the difference between them is that weak is used when variable is optional and so may be nil but an unowned variable cannot be nil and so not optional, in our example we suppose an address may not assigned to a contact so we use weak
class Contact{
var name:String!
var number:String!
weak var addr:Address? // weak refersnce
init(name:String,number:String){
self.name = name
self.number = number
print("Contact initialized")
}
deinit{
print("Contact deallocated!")
}
}
class Address{
var country:String!
var city:String!
var street:String!
var contact:Contact?
init(country:String,city:String,street:String){
self.country = country
self.city = city
self.street = street
print("Address initialized")
}
deinit{
print("Address deallocated!")
}
}
*ARC is supported in Xcode 4.2 for OS X v10.6 and v10.7 (64-bit applications) and for iOS 4 and iOS 5. Weak references are not supported in OS X v10.6 and iOS 4
*SRC : Strong Reference Cycle
Other situation leading to potential SRC issue
To bring In previous example we talked about two class with strong reference to each other, but it’s not the only situation causing the SRC issue , we may have a class with a variable of closure type , suppose we have a Name Class with a closure to return fullname from given name and surname like the following
class Name {
var givenName : String
var surName:String
lazy var fullName : Void->String = {
var fn = self.givenName+" "+self.surName
return fn
}
init(givenName:String,surName:String){
self.givenName = givenName
self.surName = surName
print("Initializer being called")
}
deinit{
print("Deinitializer being called")
}
}
Again somewhere in code we initialize it as below:
var name:Name? = Name(givenName: "Khosrov", surName: "Boloorian”)
name = nil// break reference
here the debug result after running code , initializer & deinitializer are both called
Everything is OK until we call fullname closure after which we again create an SRC issue , let’s add the following lines and see the result again:
let fullname = name!.fullName()
print(fullname)
name = nil
the debug result after running code , only initializer is called!
It’s because there is a a Strong Refrerence from lazy closure to class instance as pointed by self , and on other side there is another strong reference to lazy closure from class instance , we have used lazy in purpose in order to access our class instance by
Solution 1 :
class Name {
var givenName : String
var surName:String
lazy var fullName : (gn:String,sn:String)->String = {gn,sn in
var fn = gn+" "+sn
return fn
}
init(givenName:String,surName:String){
self.givenName = givenName
self.surName = surName
print("Initializer being called")
}
deinit{
print("Deinitializer being called")
}
}
In second approach we use magic brackets [weak self] or [unowned self] according to context as we have seen in previous example , this concept known as Capture List
Solution 2 :
class Name {
var givenName : String
var surName:String
lazy var fullName : Void->String = {[weak self] in
var fn = self!.givenName+" "+self!.surName
return fn
}
init(givenName:String,surName:String){
self.givenName = givenName
self.surName = surName
print("Initializer being called")
}
deinit{
print("Deinitializer being called")
}
}
Here the result after breaking name
Comparing JAVA GC (Garbage Collector) and Swift ARC :