Retain cycles unit test

Tram Ho

During application development, it is very difficult to avoid retain cycles even for experienced programmers. Retain cycles are not always easy to detect and can lead to hours of debugging. Of course there are great tools that help us make debugging easier (such as memory graph debugger, …) but the debugging process is still quite hard and time-consuming.

The key to fixing retain cycles is to discover them. This article will cover some code that you can put into unit tests to make discover retain retain cycles easier.

Simple idea

A fairly simple rule in ARC: an object will be retained if there is still at least one strong reference to it.

Similarly, we all know that a weak reference to an object will not increase the retain count and the object will release when the last strong reference ends.

Knowing the basics, we can write unit tests in such a way that we have both strong and weak references to the object we want to test. After we finish using the object, we will set the strong reference to nil and verify that the weak reference is also nil. If the weak reference is not nil at this point, then we need to find out why the object exists (this may be a retain cycle).

Let’s see what it looks like. This is a unit test without cycle testing:

To add this test to an object, we do the following:

  1. A weak var variable to hold a weak reference to the object we are checking the lifecycle.
  2. Set nil to remove strong references (line 8)
  3. Test weakSut will become nil (line 9)

Can we simplify?

Adding 3 lines as above for each object is a tedious and error-prone job. For example, you may accidentally forget any one of these steps and authentication will no longer work. We can write a few helper functions that we can add as extensions on XCTestCase allowing us to just add one more line for each object we want to test the life cycle for. First, let’s add a function that allows us to verify that an object is released after we execute a block that the caller provides. This will be useful for situations in which you have an instance attribute holding your object.

Lines 3-5 perform a bit of checking. If the subject object passes into nil, it will return a failure. Lines 7-9 are a closure called after the test has been run. Line 7 is a weak reference to the value object Line 8 is where we make the closure after () Line 9 is where we perform the validation that Our weak reference is not available

When using the helper function, our unit test becomes:

In cases where we do not have instance properties holding our object, we can write a simpler function:

The above code works because if nothing holds our object outside the scope of the test function, it will automatically be released by the fact that the only strong reference was released at the end of the scope. This makes testing even simpler:

Conclude

These two helper functions make up a simple API, hopefully helpful in helping to detect retain cycles before they become a real problem. The above code is hopefully simple enough to understand and does not need to edit too many existing tests (no subclassing etc).

Hopefully the above article will be helpful to you, the article was translated from: https://paul-samuels.com/blog/2018/11/20/unit-testing-retain-cycles/

Share the news now

Source : Viblo