Memory leaks prevention using an autoreleasepool in unit tests – onlinecode

Memory leaks prevention using an autoreleasepool in unit tests – onlinecode

In this post we will give you information about Memory leaks prevention using an autoreleasepool in unit tests – onlinecode. Hear we will give you detail about Memory leaks prevention using an autoreleasepool in unit tests – onlinecodeAnd how to use it also give you demo for it if it is necessary.

Memory leaks often happen without notice. Although best practices like using a weak reference to self inside closures help a lot, it’s usually not enough to prevent you from finding a certain memory peak during the development of a project.

We can use memory graph debugging or Xcode Instruments to help find and fix leaked and abandoned memory. However, using tools manually requires us to make it part of our regular workflow to prevent memory leaks from sneaking in.

Architecting SwiftUI apps with MVC and MVVMAlthough you can create an app simply by throwing some code together, without best practices and a robust architecture, you’ll soon end up with unmanageable spaghetti code. Learn how to create solid and maintainable apps with fewer bugs using this free guide.

Making it easy to test your app for memory leaks prevents us from introducing them without notice. We will not avoid all leaks, but it will decrease the chance. A good practice is to write a unit test reproducing a memory leak you encountered to ensure the memory leak does not return in the future. Let’s see how this works.

Avoiding memory leaks using unit tests

Writing a unit test by combining a weak reference with an autoreleasepool makes it easy to verify deallocation. You can see this technique as ascertaining that the deinit of a class executes and the memory releases.

In the following example, we’re verifying that a view controller is released:

/// Ensures that the OwnedBucketViewController gets deallocated after being added to the navigation stack, then popped.
func testDeallocation() {
    assertDeallocation { () -> UIViewController in
        let bucket = Bucket()
        let viewModel = OwnedBucketViewModel(bucket: bucket)
        return OwnedBucketViewController(viewModel: viewModel)
    }
}

This test will fail if any logic within the testing view controller retains the given view controller.

The extension method makes it effortless to add deallocation tests to your view controller test classes. It’s a great way to verify that your view controllers get released correctly during development.

The assert deallocation method is an extension method on XCTestCase and looks as follows:

extension XCTestCase {

    /// Verifies whether the given constructed UIViewController gets deallocated after being presented and dismissed.
    ///
    /// - Parameter testingViewController: The view controller constructor to use for creating the view controller.
    func assertDeallocation(of testedViewController: () -> UIViewController) {
        weak var weakReferenceViewController: UIViewController?

        let autoreleasepoolExpectation = expectation(description: "Autoreleasepool should drain")
        autoreleasepool {
            let rootViewController = UIViewController()

            // Make sure that the view is active and we can use it for presenting views.
            let window = UIWindow(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
            window.rootViewController = rootViewController
            window.makeKeyAndVisible()

            /// Present and dismiss the view after which the view controller should be released.
            rootViewController.present(testedViewController(), animated: false, completion: {
                weakReferenceViewController = rootViewController.presentedViewController
                XCTAssertNotNil(weakReferenceViewController)

                rootViewController.dismiss(animated: false, completion: {
                    autoreleasepoolExpectation.fulfill()
                })
            })
        }
        wait(for: [autoreleasepoolExpectation], timeout: 10.0)
        wait(for: weakReferenceViewController == nil, timeout: 3.0, description: "The view controller should be deallocated since no strong reference points to it.")
    }
}

The extension creates a weak reference to the view controller produced by the closure. After that, we present and dismiss the created view controller to ensure all lifecycle events execute correctly and the logic represents an actual in-app usage. Finally, we’re verifying that the weak reference resets to nil.

To validate that our weak reference resets to nil we’re using another extension method on XCTestCase which is a convenient method to check for a particular condition periodically:

extension XCTestCase {

    /// Checks for the callback to be the expected value within the given timeout.
    ///
    /// - Parameters:
    ///   - condition: The condition to check for.
    ///   - timeout: The timeout in which the callback should return true.
    ///   - description: A string to display in the test log for this expectation, to help diagnose failures.
    func wait(for condition: @autoclosure @escaping () -> Bool, timeout: TimeInterval, description: String, file: StaticString = #file, line: UInt = #line) {
        let end = Date().addingTimeInterval(timeout)

        var value: Bool = false
        let closure: () -> Void = {
            value = condition()
        }

        while !value && 0 < end.timeIntervalSinceNow {
            if RunLoop.current.run(mode: RunLoop.Mode.default, before: Date(timeIntervalSinceNow: 0.002)) {
                Thread.sleep(forTimeInterval: 0.002)
            }
            closure()
        }

        closure()

        XCTAssertTrue(value, "Timed out waiting for condition to be true: "(description)"", file: file, line: line)
    }
}

An alternative would be to use a block-based predicate, but these only check conditions once a second and make your tests much slower.

The XCTest API allows creating expectations for notifications, predicates, or key-value observation, but none of these make it possible to validate that the weak reference eventually resets to nil. Our new extension method does make this possible and checks for the given amount of time whether the condition matches.

The use of an autoreleasepool

Without the autoreleasepool, we would’ve not been able to verify the release of weak references. All the references within the autoreleasepool closure should be released when the pool drains.

You can see an autoreleasepool as a local context or a local container. Everything defined within this container should release once the container goes out of context. Any strong references will prevent an object from releasing, causing the memory to leak.

Catching memory leaks for regular objects

For this article, I teamed up with Vincent Pradeilles, who covered a similar topic in one of his videos. Using a similar approach as demonstrated in this article for view controllers, we can test for the deallocation of regular objects. Structs are excluded in this case since they’re value types. If you’re new to value types, I encourage you to read Struct vs classes in Swift: The differences explained.

The following extension defines another assertDeallocation method, but this time it takes a closure returning a generic AnyObject:

extension XCTestCase {

    func assertDeallocation<T: AnyObject>(of object: () -> T) {
        weak var weakReferenceToObject: T?

        let autoreleasepoolExpectation = expectation(description: "Autoreleasepool should drain")

        autoreleasepool {
            let object = object()

            weakReferenceToObject = object

            XCTAssertNotNil(weakReferenceToObject)

            autoreleasepoolExpectation.fulfill()
        }

        wait(for: [autoreleasepoolExpectation], timeout: 10.0)
        wait(for: weakReferenceToObject == nil, timeout: 3.0, description: "The object should be deallocated since no strong reference points to it.")
    }
}

The technique is similar to our view controller extension method. Only the presenting logic is missing which is why you can’t use it for view controllers. Combining the two deallocations assert methods will allow you to catch many retain issues through unit tests.

More on memory debugging

WWDC dedicated quite a few sessions to memory debugging. For example, the iOS Memory Deep Dive session of WWDC 2018 helps you understand the actual memory cost of an image and gives you tips and tricks for reducing an app’s memory footprint.

Architecting SwiftUI apps with MVC and MVVMAlthough you can create an app simply by throwing some code together, without best practices and a robust architecture, you’ll soon end up with unmanageable spaghetti code. Learn how to create solid and maintainable apps with fewer bugs using this free guide.

Conclusion

Memory leaks and retain cycles can be a pain to debug, but you’ll be better at detecting them early on with proper techniques. We can use unit tests to assert deallocations for objects and view controllers to ensure not to introduce any new retain cycles during development.

If you like to improve your Swift knowledge, check out the Swift category page. Feel free to contact me or tweet me on Twitter if you have any additional tips or feedback.

Thanks!

 

Hope this code and post will helped you for implement Memory leaks prevention using an autoreleasepool in unit tests – onlinecode. if you need any help or any feedback give it in comment section or you have good idea about this post you can give it comment section. Your comment will help us for help you more and improve us. we will give you this type of more interesting post in featured also so, For more interesting post and code Keep reading our blogs

For More Info See :: laravel And github

We're accepting well-written guest posts and this is a great opportunity to collaborate : Contact US