Swift XCTest: Verify proper deallocation of weak variables - swift

Recently I was attempting to verify that an object I wrote properly deallocates using a unit test. I found however that no matter what I tried the object would not deallocate before the test completed. So I reduced the test to a trivial example (seen below) that attempts to prove the basics of object deallocation using weak variables.
In my mind, the strong reference should stop retaining the object after the test method exits, and the weak reference should be nil when referenced on the next run loop. However, the weak reference is never nil and both tests fail. Am I misunderstanding something here? Below are the unit tests in full.
class Mock { //class type, should behave with reference semantics
init() { }
}
class DeallocationTests: XCTestCase {
func testWeakVarDeallocation() {
let strongMock = Mock()
weak var weakMock: Mock? = strongMock
let expt = expectation(description: "deallocated")
DispatchQueue.main.async {
XCTAssertNil(weakMock) //This assertion fails
expt.fulfill()
}
waitForExpectations(timeout: 1.0, handler: nil)
}
func testCaptureListDeallocation() {
let strongMock = Mock()
let expt = expectation(description: "deallocated")
DispatchQueue.main.async { [weak weakMock = strongMock] in
XCTAssertNil(weakMock) //This assertion also fails
expt.fulfill()
}
waitForExpectations(timeout: 1.0, handler: nil)
}
}
I thought that maybe deallocation was being deferred somehow by XCTest, but even wrapping the test method bodies in an autoreleasepool did not cause the object to deallocate.

The problem is that your testWeakVarDeallocation() function hasn't exited when the dispatchAsync block is called so a strong reference to strongMock is still held.
Try it like this (allowing testWeakVarDeallocation() to exit) and you'll see weakMock becomes nil as expected:
class weakTestTests: XCTestCase {
var strongMock: Mock? = Mock()
func testWeakVarDeallocation() {
weak var weakMock = strongMock
print("weakMock is \(weakMock)")
let expt = self.expectation(description: "deallocated")
strongMock = nil
print("weakMock is now \(weakMock)")
DispatchQueue.main.async {
XCTAssertNil(weakMock) // This assertion fails
print("fulfilling expectation")
expt.fulfill()
}
print("waiting for expectation")
self.waitForExpectations(timeout: 1.0, handler: nil)
print("expectation fulfilled")
}
}

Related

NotificationCenter observer is “mutated after capture by sendable closure”

Consider this simple class:
import Foundation
class ExampleClass {
init() {
let notificationCenter = NotificationCenter.default
var observer: NSObjectProtocol? = nil
// A warning is emitted for the next line
observer = notificationCenter.addObserver(forName: .NSExtensionHostDidEnterBackground,
object: nil,
queue: nil) { [weak self] _ in
self?.doSomething()
notificationCenter.removeObserver(observer!)
}
}
func doSomething() {
print("We got the notification")
}
}
This code uses the exact pattern that Apple suggests in their documentation for NotificationCenter.addObserver(forName:object:queue:using:), where the NotificationCenter gives us some opaque token that conforms to NSObjectProtocol, and we later use that token to remove the observer.
Recently, though, this code has started to produce a warning. On the line where observer is assigned, the compiler complains that
'observer' mutated after capture by sendable closure
I understand where the compiler is coming from here; if observer is a value type, then the closure will indeed get an “old” version of it. In this case, though, what we get back from addObserver() does seem to be a reference type, because the code works fine. (It’s unfortunate that Apple doesn’t give us a more specific return type for that method.)
Does this warning indicate an actual problem in this case? If so, what’s the best alternative pattern to use?
You can store the observer on the object itself:
import Foundation
class ExampleClass {
private var observer: NSObjectProtocol?
init() {
let notificationCenter = NotificationCenter.default
observer = notificationCenter.addObserver(forName: .NSExtensionHostDidEnterBackground,
object: nil,
queue: nil) { [weak self] _ in
if let self {
self.doSomething()
notificationCenter.removeObserver(self.observer!)
}
}
}
func doSomething() {
print("We got the notification")
}
}
This workaround silences the warning, and it shouldn’t change the semantics of the program in a meaningful way. It’s a bit less elegant, in that a variable that could have stayed local to init (and its closures) is now exposed to the entire class, but it should have the same effect as the previous version of the code.

Why am I getting a retain cycle from passing weak self in to a static function's closure?

My AchievementViewController does not get released from memory unless I comment out the function below
NetworkConnection.achievementList(for: -1) { [weak self] response in
guard let sections = response.object as? [AchievementListSection] else {
return print("Network failure")
}
self?.sections = sections
self?.configureCollectionView()
}
The definition for this function is below where at present we just use a delayed async call to send a stubbed response.
static func achievementList(for identifier: Int64, responseHandler: RequestResponseClosure?) {
let stubResponse = ResponseObject(object: AchievementListSection.exampleList as NSArray, code: .success)
let randomDelayMilliseconds = Int(arc4random_uniform(1000))
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(randomDelayMilliseconds)) {
responseHandler?(stubResponse)
}
}
Where exactly is self being retained to create a cycle here? It's passed in to the NetworkConnection closure as a weak reference and in turn when this closure is passed to DispatchQueue I would expect it to release after the delay has passed.
try comment this line
self?.configureCollectionView()
mb it's the problem, because [weak self] is enough for fix retain in this closure

Why isn't my method getting called?

I have a viewController communicating with DownloaderHandler using DownloaderDelegate protocol.
My protocol is defined as:
protocol DownloaderDelegate : class {
func didReceive(data:Data)
}
I have a viewController
class ViewController: UIViewController {
weak var downloadHandler : DownloaderHandler?
override func viewDidLoad() {
super.viewDidLoad()
downloadHandler = DownloaderHandler()
downloadHandler?.delegate = self
changeBackground()
}
func changeBackground (){
let googleURL = URL(fileURLWithPath: "https://www.google.com/doodle4google/images/splashes/featured.png")
print(googleURL)
downloadHandler?.downloadData(url:googleURL) // Line BB
}
}
extension ViewController : DownloaderDelegate{
func didReceive(data: Data) {
let image = UIImage(data: data)
let imageView = UIImageView(image: image!)
view.insertSubview(imageView, at: 0)
}
}
And I have a Delegating class as :
class DownloaderHandler : NSObject, URLSessionDelegate{
weak var delegate :DownloaderDelegate?
var downloadsSession: URLSession = {
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration)
return session // Line AA
}()
func downloadData(url: URL){ // Line CC
downloadsSession.dataTask(with: url){ data, response, error in
print("error is \(error), data is \(data) and response is \(response)") // Line DD
if let badError = error {
print(" the url didn't succeeed error is \(badError.localizedDescription)")
}
else if let someResponse = response as? HTTPURLResponse {
if someResponse.statusCode == 200{
self.delegate?.didReceive(data: data!)
}
}
}
}
}
Using breakpoints: Line AA, gets loaded. Line BB calls. Line CC never gets called. Why? What am I doing wrong?!
You have declared:
weak var downloadHandler : DownloaderHandler?
Then you say:
downloadHandler = DownloaderHandler()
downloadHandler is a weak reference, and nothing else retains this DownloaderHandler instance, so it vanishes in a puff of smoke after it is created. Your logging shows it being created, but if you were to log on its deinit you would also see it vanish immediately afterward. By the time you say downloadHandler?.downloadData(url:googleURL), your downloadHandler reference is nil and so nothing happens; you are talking to nobody at that point.
[You are probably slavishly following a mental rule that delegate references should be weak. But that rule is predicated on the assumption that the delegate has an independent existence, and thus should not be "owned" by the referrer. This object, however, has no independent existence; it is more a decorator object (what I would call a Helper). Thus, the reference needs to be strong. The back-reference is still weak, so you won't get a circular retain cycle.]
Remove the "weak" qualifier from the downloadHandler property on your view controller.
As it is the only reference to the downloadHandler object, it will be removed from memory as soon as the viewDidLoad method finishes executing.
You can make a small test; add a breakpoint to line BB and check if downloadHandler has a value. I suspect it will be "nil", because it is a weak property.

Cannot load view using NSNib

Anyone knows the correct way to instantiate a view using NSNib in Swift ?
The code below crashes.
import Cocoa
class ViewController: NSViewController {
var myview:NSView!
override func viewDidLoad() {
super.viewDidLoad()
let obj = AutoreleasingUnsafeMutablePointer<NSArray?>()
if let success = NSNib(nibNamed: "View", bundle: NSBundle.mainBundle())?.instantiateWithOwner(self, topLevelObjects: obj) where success {
if let m = obj.memory { // fatal error:
myview = m[0] as! NSView
}
}
}
}
You are seeing the fatal error there because the value you are trying to access is nil and you are forcefully trying to unwrap the optional. You should safely unwrap optionals this is what they are meant for. So, always use if statement when you try to access optionals, then you won't get this error.

Strong Reference Cycles for Closures?

Swift Closure will have a strong reference cycle when it refers to self like this example:
class Test {
var name = "Hello"
func doSomething() {
{() -> Void in
self.name = "otherName"
}()
}
}
In the previous example, I created a strong reference cycle so I have to fix it with:
class Test {
var name = "Hello"
func doSomething() {
{[unowned self] () -> Void in
self.name = "otherName"
}()
}
}
Question: If I refer self in a closure do I have to use alway unowned self or are there cases where I have to use weak self?
If I refer self in a closure do I have to use alway unowned self or are there cases where I have to use weak self?
Neither. In most cases, just refer to self normally and do nothing with its memory management. You only have to worry about memory management if there is a danger of a retain cycle, and unless you store the closure somewhere, such as a property of self, there is no such danger.
You can easily prove this by adding a deinit implementation:
class Test {
var name = "Hello"
func doSomething() {
{() -> Void in
self.name = "otherName"
}()
}
deinit {
println("bye")
}
}
Now make a Test instance and immediately release it:
func testTest () {
let t = Test()
}
You see "bye" in the console, proving that the instance was released in good order. There was never any kind of "strong reference cycle" in this code. Your concerns are groundless.
[By the way, you are using the word "closure" wrong. Every Swift function is a closure. If there were a retain cycle issue merely because of using the word self in a closure, every Swift function would be subject to this issue - and clearly that is not the case. The place where weak and unowned self comes into play is in an anonymous function - and only, as I said before, if that anonymous function is itself also retained by self.]