Search code examples
iosswiftavplayerretain-cycleavplayerlayer

Creating AVPlayerLayer prevents releasing AVPlayer


If I ever set an AVPlayerLayer, then there will be some retain cycle that will prevent deinit from ever being called.

import AVFoundation

class MyPlayer: AVPlayer {

    fileprivate(set) lazy var playerLayer: AVPlayerLayer = {
        // Create a player layer
        $0.videoGravity = AVLayerVideoGravityResizeAspectFill
        $0.backgroundColor = UIColor.black.cgColor
        return $0
    }(AVPlayerLayer(player: self))

    override init() {
        super.init()
        print("MyPlayer init")

        _ = playerLayer
    }

    deinit {
        print("MyPlayer deinit")
    }
}

Testing with this, only "MyPlayer init" will be printed:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

    _ = MyPlayer()

    return true
}

Solution

  • AVPlayerLayer is keeping a strong reference to the player, so you shouldn't keep a strong reference of the playerLayer from the player itself.

    Solution 1

    If you don't plan to remove the sublayer, then the superlayer will keep the reference for you, so you can use weak:

    private weak var _playerLayer: AVPlayerLayer?
    var playerLayer: AVPlayerLayer! {
        if let p = _playerLayer {
            return p
        }
        let p: AVPlayerLayer = {
            // Create a player layer
            $0.videoGravity = AVLayerVideoGravityResizeAspectFill
            $0.backgroundColor = UIColor.black.cgColor
            return $0
        }(AVPlayerLayer(player: self))
        _playerLayer = p
        return p
    }
    

    Solution 2

    If you plan to remove and re-add the sublayer, then you need the strong reference variable to be made in your UIView, UIViewController or some other manager for the playerLayer.