Understanding ARC’s Effect On Your App’s Performance

This requires programmers to know a bit think more about how they implement their programs, or we could create retain cycles that ARC is not capable of handling.Allocation of ObjectsWhen allocating a class object in Swift (only class objects are reference counted, Value Types are off the hook!), a call is made to allocate the appropriate amount of memory on the heap..What you won’t see, is that Swift actually allocates more space than you need to store your object (2 more words, if anyone is interested in the numbers)..The extra space is used to keep a pointer to the type information and to keep a reference counter..This reference counter is what makes ARC so powerful, but it is also a feature that will make things a little bit trickier for programmers who are looking for maximum performance.Keeping Track of ReferencesWhen your object is allocated and assigned to a variable, the reference counter is incremented to 1..This indicates to the system that your object is currently in use and should remain allocated..The counter is incremented every time your share the reference to another variable, and decremented when those variables are destroyed..When the counter goes to 0, the object can be safely deallocated.The above seems simple simple enough, so why do I bring it up?Though incrementing and decrementing variables are usually fast and inexpensive operations — in the case of ARC, they are not.The reason that ARC’s operations are much slower than the regular arithmetic operations are the following:There are a few layers of indirection that needs to be dealt with before Swift can actually update the counter.References can be shared across multiple threads..This means that Swift has to have mechanisms to deal with increments and decrements happening simultaneously since two threads can’t manipulate the same variable at the same time.As you can see, this is not trivial and it will take a little bit of time to make sure that all goes according to plan..Let’s get to some code and see how this will add up!class MyClass { var a: Int init(a: Int) { self.a = a }}var A = MyClass(a: 1)var B = A// Use A// Use BThis is a very stripped down example of a class, just for simplicity..We’re allocating an object of type MyClass and assigning it to A, and then re-assign it to B..Let’s take a look at what the Swift compiler and ARC does behind the scenes for us:var A = MyClass(a: 1)var B = Aretain(B)// Use Arelease(A)// Use Brelease(B)We got three more method calls, just from that simple program..Also, remember that the retain(_:) and release(_:) calls actually take a little bit of time due to the reasons we discussed above.Let’s have a look at a slightly more realistic example and see that things can escalate very quickly:class Label { var text: String var font: UIFont init(text: String, font: UIFont) { self.text = text self.font = font }}var label1 = Label(text: "Hello", font: .systemFont(ofSize: 20))var label2 = label1// Use label1// Use label2This is a pretty straight forward label class, let’s see what happens to it when we let ARC do its thing.var label1 = Label(text: "Hello", font: .systemFont(ofSize: 20))var label2 = label1retain(label2)retain(label2.text._storage)retain(label2.font)// Use label1release(label1.text._storage)release(label1.font)release(label1)// Use label2release(label2.text._storage)release(label2.font)release(label2)Oh God!. More details

Leave a Reply