筆者曾用過Instrument檢測內(nèi)存泄漏湿刽,泄漏
并非完全能檢測
出來没卸;
幾番周折之后發(fā)覺 在每個dealloc
或者deinit
析構(gòu)函數(shù) 打印日志能解決問題戳晌,雖然笨拙但能解決在編碼時候的自我檢測規(guī)范。
廢話少說笔横,本篇介紹uber/RIBs內(nèi)存泄漏工具(LeakDetector.swift)很優(yōu)雅的解決我的需求
核心思路:
///存儲即將釋放變量
private let trackingObjects = NSMapTable<AnyObject, AnyObject>.strongToWeakObjects()
/// Sets up an expectation for the given object to be deallocated within the given time.
///
/// - parameter object: The object to track for deallocation.
/// - parameter inTime: The time the given object is expected to be deallocated within.
/// - returns: The handle that can be used to cancel the expectation.
@discardableResult
public func expectDeallocate(object: AnyObject, inTime time: TimeInterval = LeakDefaultExpectationTime.deallocation) -> LeakDetectionHandle {
expectationCount.value += 1
let objectDescription = String(describing: object)
let objectId = String(ObjectIdentifier(object).hashValue) as NSString
trackingObjects.setObject(object, forKey: objectId)
let handle = LeakDetectionHandleImpl {
self.expectationCount.value -= 1
}
Executor.execute(withDelay: time) {
// Retain the handle so we can check for the cancelled status. Also cannot use the cancellable
// concurrency API since the returned handle must be retained to ensure closure is executed.
if !handle.cancelled {
let didDeallocate = (self.trackingObjects.object(forKey: objectId) == nil)
let message = "<\(objectDescription): \(objectId)> has leaked. Objects are expected to be deallocated at this time: \(self.trackingObjects)"
if self.disableLeakDetector {
if !didDeallocate {
print("Leak detection is disabled. This should only be used for debugging purposes.")
print(message)
}
} else {
assert(didDeallocate, message)
}
}
self.expectationCount.value -= 1
}
return handle
}
總結(jié)如下:
將需要釋放的對象添加到trackingObjects竞滓,當添加的對象引用計數(shù)執(zhí)行一次-1,添加的對象在內(nèi)存中就會自動釋放吹缔,并且相應(yīng)的trackingObjects對象中的對象也會被自動移除.
接下來,在
deinit
方法調(diào)用expectDeallocate()
方法商佑,延遲一定時間檢測trackingObjects
對象的是否消失即可
so easy , look look deinit里的代碼:
///PresentableInteractor.swift
deinit {
LeakDetector.instance.expectDeallocate(object: presenter as AnyObject)
}
///Router.swift
deinit {
interactable.deactivate()
if !children.isEmpty {
detachAllChildren()
}
lifecycleSubject.onCompleted()
deinitDisposable.dispose()
LeakDetector.instance.expectDeallocate(object: interactable)
}
///ViewableRouter.swift
deinit {
LeakDetector.instance.expectDeallocate(object: viewControllable.uiviewController, inTime: LeakDefaultExpectationTime.viewDisappear)
}