Swift 4中AVPlayer循环实现问题:restartVideoFromBeginning内player为nil
Hey there! Let's break down why your player variable is turning up nil when the video finishes playing, and how to fix it:
1. Your Player Isn't Being Retained Properly
The most common culprit here is that your player instance is getting deallocated from memory before the "play to end" notification fires. This happens if:
- You declared
playeras a local variable inside a method (it gets destroyed once the method finishes running) - You used a
weakreference for the player property (it gets set to nil when there's no other strong reference holding it)
Fix: Declare player as a strong property in your view controller:
class ViewController: UIViewController { // Strong reference keeps the player alive private var player: AVPlayer? // Rest of your code... }
2. You're Listening to All AVPlayerItem Notifications
Right now, your notification observer is set up with object: nil, which means it will trigger for any AVPlayerItem that finishes playing in your app. If another player's item sends the notification, or if your original player has already been released, player will be nil.
Fix: Register the observer specifically for your player's current item, and clean up the observer when your view controller is deallocated:
// When setting up your player: if let playerItem = player?.currentItem { NotificationCenter.default.addObserver(self, selector: #selector(restartVideoFromBeginning), name: .AVPlayerItemDidPlayToEndTime, object: playerItem) } // Add this to your view controller to avoid memory leaks: deinit { NotificationCenter.default.removeObserver(self) }
3. Timing Issues with Player Initialization
If you registered the notification before you created and assigned the player variable, or if you accidentally set player = nil somewhere else in your code, the method will find nil when it runs.
Fix: Always register the notification after you've initialized the player, and avoid setting player to nil unless you intentionally want to destroy it. Here's a complete, safe setup example:
import UIKit import AVKit class ViewController: UIViewController { private var player: AVPlayer? private var playerLayer: AVPlayerLayer? override func viewDidLoad() { super.viewDidLoad() setupVideoPlayer() } private func setupVideoPlayer() { // Replace with your video URL (local or remote) guard let videoURL = Bundle.main.url(forResource: "your_video", withExtension: "mp4") else { print("Couldn't find video file!") return } let playerItem = AVPlayerItem(url: videoURL) player = AVPlayer(playerItem: playerItem) // Add player layer to your view playerLayer = AVPlayerLayer(player: player) playerLayer?.frame = view.bounds playerLayer?.videoGravity = .resizeAspectFill view.layer.addSublayer(playerLayer!) // Register notification for THIS player item NotificationCenter.default.addObserver(self, selector: #selector(restartVideo), name: .AVPlayerItemDidPlayToEndTime, object: playerItem) player?.play() } @objc func restartVideo() { guard let player = self.player else { print("Player is nil - check retention and initialization!") return } // Use CMTimeMakeWithSeconds for better readability (Swift 4+) let startCMTime = CMTimeMakeWithSeconds(0, preferredTimescale: 1) player.seek(to: startCMTime) { [weak self] _ in self?.player?.play() } } deinit { NotificationCenter.default.removeObserver(self) } }
A quick note: Using [weak self] in the seek completion block is a good practice to avoid retain cycles, especially if your view controller might be dismissed before the seek finishes.
内容的提问来源于stack exchange,提问作者123

