在10/2020中进行编辑:先前的标题为“未在UIPageViewController的UIViewController中调用Deinitializer”
我想在我的UIViewController(是UIPageViewController的一部分)中使用以下反初始化程序来删除myplayerLayer
并将其设置为player
,nil
以便内存不会过载(因为deinit
应该始终调用UIViewController,因为这样就不再需要了) ):
deinit {
self.player = nil
self.playerLayer.removeFromSuperlayer()
print("deinit")
}
为了检查是否deinit
曾经执行过,我添加了打印件,发现它从未被调用过。有人可以解释为什么不叫它吗?您会建议我做什么来实现自己想做的事情?
编辑:
按照Rob建议的问题中的说明(在注释中),我发现以下函数会导致内存泄漏。如果可以在documents目录中找到文件,则该功能应该设置播放器。
setupPlayer()函数:
//setup video player
func setupPlayer() {
//get name of file on server //self.video is a String containing the URL for a video on a server
let fileName = URL(string: self.video!)!.lastPathComponent
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
let filePath = url.appendingPathComponent(fileName)?.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath!) {
//create file with name on server if not there already
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
if let docDir = paths.first
{
let appFile = docDir.appending("/" + fileName)
let videoFileUrl = URL(fileURLWithPath: appFile)
//player's video
if self.player == nil {
let playerItemToBePlayed = AVPlayerItem(url: videoFileUrl) //AVPlayerItem(url: videoFileUrl)
self.player = AVPlayer(playerItem: playerItemToBePlayed)
//add sub-layer
playerLayer = AVPlayerLayer(player: self.player)
playerLayer.frame = self.view.frame
self.controlsContainerView.layer.insertSublayer(playerLayer, at: 0)
//when are frames actually rendered (when is video loaded)
self.player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context:nil)
//loop through video
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: nil, using: { (_) in
DispatchQueue.main.async {
self.player?.seek(to: kCMTimeZero)
self.player?.play()
}
})
}
}
}
}
pageViewController函数(viewcontrollerAfter)
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController?
{
let currentIndexString = (viewController as! MyViewController).index
let currentIndex = indec.index(of: currentIndexString!)
//set if so that next page
if currentIndex! < indec.count - 1 {
//template
let myViewController = MyViewController()
//enter data into template
myViewController.index = self.indec[currentIndex! + 1]
//return template with data
return myViewController
}
return nil
}
编辑2:
如您所见,没有回溯,请注意该malloc的大小(右上)和类似的大malloc的大小(左下)。
如果我们在“调试内存图”中查看对象图,则可以看到:
我们可以看到视图控制器是由闭包(中间路径)捕获的。我们还可以看到观察者保持着强烈的参考(那条底部的道路)。
Because I turned on "Malloc stack" feature (shown in https://stackoverflow.com/a/30993476/1271826), I could click on the "Closure Captures" and can see the stack trace in the right panel:
(Forgive me that that memory graph is slightly different than the first screen snapshot because I fixed the other memory issue, the observer, as discussed at the end of this answer.)
Anyway, if I click on the highest entry in the stack trace that's in black (i.e. the last bit of my own code in that stack trace), it takes us directly to the offending code:
That draws our attention to your original code:
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: nil, using: { (_) in
DispatchQueue.main.async {
self.player?.seek(to: kCMTimeZero)
self.player?.play()
}
})
The closure is keeping strong reference to self
. You can correct that with:
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem, queue: .main) { [weak self] _ in
self?.player?.seek(to: kCMTimeZero)
self?.player?.play()
}
Note, the [weak self]
capture list in the closure.
顺便说一句,而你并不需要nil
你player
在deinit
,你就需要删除观察员。我还将context
为您的观察者设置一个,以便您observerValue(forKeyPath:of:change:context:)
可以知道是否需要处理。
因此,可能会导致类似:
private var observerContext = 0
private weak var observer: NSObjectProtocol?
func setupPlayer() {
let fileName = URL(string: video!)!.lastPathComponent
let fileManager = FileManager.default
let videoFileUrl = try! fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent(fileName)
if fileManager.fileExists(atPath: videoFileUrl.path), player == nil {
let playerItemToBePlayed = AVPlayerItem(url: videoFileUrl)
player = AVPlayer(playerItem: playerItemToBePlayed)
//add sub-layer
playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = view.bounds
controlsContainerView.layer.insertSublayer(playerLayer, at: 0)
//when are frames actually rendered (when is video loaded)
player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: &observerContext)
//loop through video
observer = NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem, queue: .main) { [weak self] _ in
self?.player?.seek(to: kCMTimeZero)
self?.player?.play()
}
}
}
deinit {
print("deinit")
// remove loadedTimeRanges observer
player?.removeObserver(self, forKeyPath: "currentItem.loadedTimeRanges")
// remove AVPlayerItemDidPlayToEndTime observer
if let observer = observer {
NotificationCenter.default.removeObserver(observer)
}
}
// note, `observeValue` should check to see if this is something
// this registered for or whether it should pass it along to `super`
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &observerContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
// do something
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句