2017-08-25 68 views
1

我正在嘗試編寫測試來檢查保留週期,但遇到了這種奇怪的行爲。當將視圖控制器設置爲nil時,A UIViewController的屬性不會取消分配。以此模擬對象爲例:UIViewController變量在釋放後仍然存在

class BasicViewController: UIViewController { 
    var someObject = NSObject() 
    ..... 
} 

它只是一個變量。你會認爲當致電basicViewController = nil會導致someObject爲零,但它不是。

it("releases someObject") { 
    var controller: MockController? = MockController() 
    weak var something = controller?.something 
    expect(controller).toNot(beNil()) 
    controller = nil 
    expect(controller).to(beNil()) 
    expect(something).to(beNil()) 
} 

it("doesn't release someObject") { 
    var controller: MockController? = MockController() 
    weak var something = controller?.something 
    expect(controller).toNot(beNil()) 
    _ = controller?.view 
    controller = nil 
    expect(controller).to(beNil()) 
    expect(something).toNot(beNil()) 
} 

當調用vc.view這個調用loadView還有UIViewController的生命週期功能 - viewDidLoadviewDidAppearviewWillAppear。我的問題是爲什麼?爲什麼當我引用UIViewControllerview屬性時,即使在將UIViewController設置爲nil後,所有對象UIViewController仍然存在。

FWIW,我使用QuickNimble進行測試,以及Swift 3.1

+0

但是,如果您查看兩個測試,兩者之間的唯一區別是我調用_ = vc.view,這導致我必須使用toNot()來使測試通過。這是不希望的。我創建了這個例子來展示最簡單的對象如何導致這種持久性的發生。沒有強大的引用週期發生,有些東西只是一個NSObject。它讓我相信,當與視圖交互時,控制器的屬性會持續存在,但最終會在一段時間後解除分配。 – jsetting32

回答

1

簡短的回答:

添加autoreleasepool,當對象從池耗盡,這將精確地決定,並且它可以作爲你會預料到的。


龍答:

我遇到你描述相同的行爲。但問題不是視圖控制器的屬性。這是視圖控制器本身。

在您的示例中,您將controller設置爲nil,並使用現在是nil的事實來推斷控制器是否已解除分配。但這只是測試對視圖控制器的特定引用是否爲nil,但視圖控制器本身可能尚未釋放。但是您可以使用您的視圖控制器本身的weak var測試。考慮這個視圖控制器:

class BasicViewController: UIViewController { 
    // this is intentionally blank 
} 

,其視圖控制器,體現的是您所描述的行爲,其中裝載的觀點之後XCTAssertNil測試失敗,我可以寫測試:

class MyApp2Tests: XCTestCase { 

    func testWithoutView() { 
     var controller: BasicViewController? = BasicViewController() 
     weak var weakController = controller 
     XCTAssertNotNil(weakController) 
     controller = nil 
     XCTAssertNil(weakController)   // this succeeds 
    } 

    func testWithView() { 
     var controller: BasicViewController? = BasicViewController() 
     weak var weakController = controller 
     XCTAssertNotNil(weakController) 
     controller?.loadViewIfNeeded() 
     controller = nil 
     XCTAssertNil(weakController)   // this fails 
    } 

} 

但是,當我添加了一個autoreleasepool明確地控制池何時耗盡,它如預期般工作:

func testWithViewAndAutoreleasePool() { 
    weak var weakController: BasicViewController? 
    autoreleasepool { 
     var controller: BasicViewController? = BasicViewController() 
     weakController = controller 
     XCTAssertNotNil(weakController) 
     controller?.loadViewIfNeeded() 
     controller = nil 
    } 
    XCTAssertNil(weakController)   // this succeeds 
} 

順便說一句,如果您正在尋找另外l確認視圖控制器本身的釋放時機,在deinit(以及您設置的controller = nil)中添加一條print語句,您會看到deinit的時間在做任何加載視圖。

我無法解釋這種行爲。爲什麼要用view做一些事情會影響視圖控制器的生命週期?順便說一句,我也使用視圖控制器的屬性執行上述測試,就像你的問題一樣,我看到完全相同的行爲(但恕我直言,這並不令人驚訝,因爲它只是因爲視圖控制器本身沒有被釋放)。

至少我們可以用autoreleasepool明確控制自動釋放池的生命週期時序。

相關問題