如何测试对象是否被释放?

问题描述 投票:0回答:2

我有一些执行保留循环的模拟代码(或者至少在我看来它应该)。这是:

protocol MyService {
    func perform(completion: @escaping () -> Void)
}

class MyServiceImpl: MyService {
    func perform(completion: @escaping () -> Void) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: completion)
    }
}

class MyObject {
    let service: MyService
    var didComplete = false

    init(service: MyService) {
        self.service = service
    }

    func doSomething(completion: @escaping () -> Void) {
        service.perform {
            self.didComplete = true
            completion()
        }
    }
}

注意,在

MyObject.doSomething()
方法中,我们在服务完成中强烈捕获
self
。这应该会导致保留周期,因为
MyObject
持有对
service
的引用,而
service
持有对
MyObject
的引用。 (如果我说错了请多多指教。

接下来,我们编写测试来捕获此内存泄漏:

final class DemoTests: XCTestCase {

    func test_demo() {
        let service = MyServiceImpl()
        let myObject = MyObject(service: service)
        addTeardownBlock { [weak myObject, weak service] in
            XCTAssertNil(myObject)
            XCTAssertNil(service)
        }
        let exp = expectation(description: "wait for complete")
        
        myObject.doSomething {
            exp.fulfill()
        }
        
        wait(for: [exp], timeout: 1)
        XCTAssertTrue(myObject.didComplete)
    }
}

这次测试是通过。不应该。

我做错了什么,或者我对保留循环或

XCTest
框架不了解什么?

谢谢您的帮助!

swift xctest retain-cycle xctestcase
2个回答
0
投票

这里没有保留周期。该闭包确实强烈地捕获了

self
,并且
self
也保留了对
MyServiceImpl
的强烈引用。然而,当闭包传递给
MyServiceImpl
时,
MyServiceImpl
不会保留闭包的强引用。它只是将其传递给
DispatchQueue
,后者会在运行完成后立即丢弃闭包。该图看起来像这样:

DispatchQueue.main ---> closure ---> MyObject ----> MyServiceImpl

要获得保留周期,

MyService
可以保留对闭包的引用:

class MyServiceImpl: MyService {
    
    var closure: (() -> Void)?
    func perform(completion: @escaping () -> Void) {
        closure = completion // note this line
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: completion)
    }
}

现在有一个保留周期,您的测试失败了。您还可以在 Xcode 的内存图调试器中看到这一点。


0
投票

没有保留周期。

DispatchQueue.main.asyncAfter
执行后将释放闭包,然后引用计数器达到0并且闭包被释放,并且引用计数也减少到
myObject

© www.soinside.com 2019 - 2024. All rights reserved.