一名观察员被释放或砸了

时间:2018-10-11 17:26:48

标签: ios key-value-observing

我在第一次启动该应用程序时遇到了崩溃

  

KVO_IS_RETAINING_ALL_OBSERVERS_OF_THIS_OBJECT_IF_IT_CRASHES_AN_OBSERVER_WAS_OVERRELEASED_OR_SMASHED

但是第二次打开应用程序时,它不会崩溃

Firebase Crashlytics日志:

Crashed: com.apple.main-thread
0  libobjc.A.dylib                0x1fa2d7dac object_isClass + 16
1  Foundation                     0x1fbb7fbd8 KVO_IS_RETAINING_ALL_OBSERVERS_OF_THIS_OBJECT_IF_IT_CRASHES_AN_OBSERVER_WAS_OVERRELEASED_OR_SMASHED + 68
2  Foundation                     0x1fbb7d36c NSKeyValueWillChangeWithPerThreadPendingNotifications.llvm.6024598272766318604 + 304
3  AVFoundation                   0x201146074 __avplayeritem_fpItemNotificationCallback_block_invoke + 5800
4  libdispatch.dylib              0x1fab436c8 _dispatch_call_block_and_release + 24
5  libdispatch.dylib              0x1fab44484 _dispatch_client_callout + 16
6  libdispatch.dylib              0x1faaf09ec _dispatch_main_queue_callback_4CF$VARIANT$mp + 1068
7  CoreFoundation                 0x1fb09a1bc __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
8  CoreFoundation                 0x1fb095084 __CFRunLoopRun + 1964
9  CoreFoundation                 0x1fb0945b8 CFRunLoopRunSpecific + 436
10 GraphicsServices               0x1fd308584 GSEventRunModal + 100
11 UIKitCore                      0x227adb558 UIApplicationMain + 212
12 Boxit4me                       0x102a59ca0 main (FAQVC.swift:29)
13 libdyld.dylib                  0x1fab54b94 start + 4

enter image description here

我该如何解决此崩溃?

更新: 我认为这与我的SignupViewController有关:

import UIKit
import AVFoundation
import AVKit
import NVActivityIndicatorView
import Localize_Swift
import IQKeyboardManagerSwift
class BaseSignUpViewController: BaseViewController {

    @IBOutlet weak var mainView: UIView!
    @IBOutlet weak var alreadySignInLabel: UILabel!
    @IBOutlet weak var businessAccountBtn: UIButton!
    @IBOutlet weak var personalAccountBtn: UIButton!
    @IBOutlet weak var signupAsLabel: UILabel!
    @IBOutlet weak var videoView: UIView!
    @IBOutlet weak var vwVideoContainer : UIView!
    @IBOutlet weak var vwSignUpView : UIView!
    @IBOutlet weak var btnSignUp : UIButton!
    @IBOutlet weak var lblHowItWorks : UILabel!
    @IBOutlet weak var btnFullScreenVideo: UIButton!
    var player: AVPlayer?
    var activityIndicator : NVActivityIndicatorView!


    @IBOutlet var rapidLabel: IGUILabel!
    @IBOutlet weak var thumbnailImage: UIImageView!
    @IBOutlet weak var playVideoButton: UIButton!


    class func initializeViewController() -> UINavigationController {
        return (StoryBoard.SignUpFlow.storyboard().instantiateInitialViewController() as? UINavigationController)!
    }

    class func initializeViewBaseController() -> BaseSignUpViewController {
        return StoryBoard.SignUpFlow.storyboard().instantiateViewController(withIdentifier: SegueIdentifiers.SignUpFlow.BaseSignUpViewController) as! BaseSignUpViewController
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        IQKeyboardManager.shared.shouldResignOnTouchOutside = true
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        setUIText()
        AppUtility.setUpNavigationBar()

        self.navigationController?.setNavigationBarHidden(true, animated: true)
//        self.navigationController?.isNavigationBarHidden = true


        if (UserDefaults.standard.object(forKey: "LCLCurrentLanguageKey") as? String) == nil {
            self.performSegue(withIdentifier: SegueIdentifiers.SignUpFlow.LanguageSelection, sender: nil)
        }


        self.btnFullScreenVideo.isHidden = true
        self.thumbnailImage.isHidden = false
        self.playVideoButton.isHidden = false
    }

    override func viewDidAppear(_ animated: Bool) {

        self.showVideoView()

        NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem)

    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidAppear(animated)

    }
    override func setUIText() {

        if(Localize.currentLanguage() == "ar"){
            UIView.appearance().semanticContentAttribute = .forceRightToLeft
            UINavigationBar.appearance().semanticContentAttribute = .forceRightToLeft
            self.btnSignUp.semanticContentAttribute = .forceRightToLeft
            self.btnSignUp.titleEdgeInsets = UIEdgeInsetsMake(0, -20, 0, 0)
            self.thumbnailImage.image = getThumbnailImage(forUrl: URL(string: Constants.websiteBaseURL + "Content/Theme/videos/how_it_works_ar.mp4")!)
            self.player = AVPlayer(url: URL(string: Constants.websiteBaseURL + "Content/Theme/videos/how_it_works_ar.mp4")!)

        }else{
            UIView.appearance().semanticContentAttribute = .forceLeftToRight
            UINavigationBar.appearance().semanticContentAttribute = .forceLeftToRight
            self.btnSignUp.semanticContentAttribute = .forceLeftToRight
            self.btnSignUp.titleEdgeInsets = UIEdgeInsetsMake(0, 20, 0, 0)
            self.thumbnailImage.image = getThumbnailImage(forUrl: URL(string: Constants.websiteBaseURL + "Content/Theme/videos/how_it_works.mp4")!)
            self.player = AVPlayer(url: URL(string: Constants.websiteBaseURL + "Content/Theme/videos/how_it_works.mp4")!)
        }
        self.showVideoView()

        self.btnSignUp.titleLabel?.font = Font.Regular.fontWithSize(21)
        self.btnSignUp.setTitle("Signup As".localized(), for: .normal)

        self.businessAccountBtn.setTitle("Premium Account".localized(), for: .normal)
        self.personalAccountBtn.setTitle("Personal Account (Free)".localized(), for: .normal)
        self.businessAccountBtn.titleLabel?.font = Font.Regular.fontWithSize(13) //UIFont().textFieldText()
        self.personalAccountBtn.titleLabel?.font = Font.Regular.fontWithSize(13)

        self.lblHowItWorks.text = "How it works".localized()
        self.lblHowItWorks.font = Font.Regular.fontWithSize(20)
        setupSignInLabel()
        setupGestures()
        setupMainView()
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let objLanguagePreferenceVC = segue.destination as? LanguagePreferenceVC {
            objLanguagePreferenceVC.languageChanged = {
                self.setUIText()
            }

        }
        if let objSignUp = segue.destination as? SignUpViewController {
            if (sender as! UIButton) == self.personalAccountBtn {
                objSignUp.accountType = AccountType.Personal
            }
            else{
                objSignUp.accountType = AccountType.Premium
            }
        }
    }

    @IBAction func btnSignUpClicked(_ sender : UIButton){
        self.performSegue(withIdentifier: SegueIdentifiers.SignUpFlow.SignUpSegue, sender: sender)
    }

    @IBAction func videoViewTapped(_ sender : UITapGestureRecognizer){
        if self.player?.timeControlStatus == .playing {
            self.player?.pause()
            self.playVideoButton.isHidden = false
            self.btnFullScreenVideo.isHidden = true
        }
        else{
            self.player?.play()
            self.btnFullScreenVideo.isHidden = false
            self.playVideoButton.isHidden = true
        }
    }


    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(true)
        self.player?.pause()
        let playerItem = self.player?.currentItem

        do{try self.removeObserver(self, forKeyPath: "actionAtItemEnd")}catch{}

        do{try playerItem?.removeObserver(self, forKeyPath: "playbackBufferEmpty")}catch{}

        do{try playerItem?.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")}catch{}

        do{try playerItem?.removeObserver(self, forKeyPath: "playbackBufferFull") }catch{}

        self.player = nil
    }

    func setupSignInLabel(){

        let italicAttribute = [NSAttributedStringKey.font:Font.Regular.fontWithSize(13), NSAttributedStringKey.foregroundColor: Color.blueColor.value]
        let nextDropString = NSAttributedString(string: "Already have an account?".localized(), attributes: italicAttribute)

        let colorFontAttributes = [NSAttributedStringKey.font:Font.Regular.fontWithSize(13) , NSAttributedStringKey.foregroundColor: Color.theme.value]
        let timerString = NSAttributedString(string: " " + "Login".localized(), attributes: colorFontAttributes)


        let labelString = NSMutableAttributedString(attributedString: nextDropString)

        labelString.append(timerString)

        self.alreadySignInLabel.attributedText = labelString
    }

    func setupGestures()  {
        self.alreadySignInLabel.isUserInteractionEnabled = true
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(loadSignIn(_:)))
        self.alreadySignInLabel.addGestureRecognizer(tapGesture)
    }

    func setupMainView(){
        self.mainView.layer.masksToBounds = false;
        self.mainView.layer.shadowOffset = CGSize.init(width: 0, height: 0)
        self.mainView.layer.shadowOpacity = 0.4;
        self.mainView.layer.shadowColor = UIColor.lightGray.cgColor

        self.vwSignUpView.layer.masksToBounds = false;
        self.vwSignUpView.layer.shadowOffset = CGSize.init(width: 0, height: 0)
        self.vwSignUpView.layer.shadowOpacity = 0.4;
        self.vwSignUpView.layer.shadowColor = UIColor.lightGray.cgColor

        self.vwVideoContainer.layer.borderColor = UIColor.gray.withAlphaComponent(0.8).cgColor
        self.vwVideoContainer.layer.borderWidth = 0.5
        self.vwVideoContainer.layer.masksToBounds = false;
        self.vwVideoContainer.layer.shadowOffset = CGSize.init(width: 0, height: 0)
        self.vwVideoContainer.layer.shadowOpacity = 0.1;
        self.vwVideoContainer.layer.shadowColor = UIColor.lightGray.cgColor



    }

    func showVideoView(){

        addObserver(self, forKeyPath: "actionAtItemEnd", options: [], context: nil)
        let playerItem = self.player?.currentItem
        playerItem?.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .new, context: nil)
        playerItem?.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .new, context: nil)
        playerItem?.addObserver(self, forKeyPath: "playbackBufferFull", options: .new, context: nil)
        // create a video layer for the player
        let layer: AVPlayerLayer = AVPlayerLayer(player: player)
        layer.backgroundColor = UIColor.clear.cgColor //Color.custom(hexString: "#F9F9F9", alpha: 1.0).value.cgColor
        // make the layer the same size as the container view
        layer.frame = videoView.bounds

        let xAxis = self.videoView.center.x
        let yAxis = self.videoView.center.y

        let frame = CGRect(x: (xAxis - 23), y: (yAxis - 23), width: 45, height: 45)
        activityIndicator = NVActivityIndicatorView(frame: frame)
        activityIndicator.type = .ballPulse // add your type
        activityIndicator.color = Color.theme.value// add your color


        // make the video fill the layer as much as possible while keeping its aspect size
        layer.videoGravity = AVLayerVideoGravity.resizeAspectFill

        // add the layer to the container view
        videoView.layer.addSublayer(layer)
        self.videoView.addSubview(activityIndicator)
    }

    @objc func loadSignIn(_ sender : Any){
        //self.goBack()
        let businessSignupVC = SignInViewController(nibName: "SignInViewController", bundle: nil)
        self.navigationController?.pushViewController(businessSignupVC, animated: true)
    }

    @IBAction func businessAccountBtnPressed(_ sender: Any) {
        let businessSignupVC = BussninessSignupViewController(nibName: "BussninessSignupViewController", bundle: nil)
        self.navigationController?.pushViewController(businessSignupVC, animated: true)
    }

    @IBAction func personalAccountBtnPressed(_ sender: Any) {
        let personalSignupVC = PersonalSignupViewController(nibName: "PersonalSignupViewController", bundle: nil)
        self.navigationController?.pushViewController(personalSignupVC, animated: true)

    }
    func getThumbnailImage(forUrl url: URL) -> UIImage? {
        let asset: AVAsset = AVAsset(url: url)
        let imageGenerator = AVAssetImageGenerator(asset: asset)

        do {
            let thumbnailImage = try imageGenerator.copyCGImage(at: CMTimeMake(32, 60) , actualTime: nil)
            self.thumbnailImage.image  =  UIImage(cgImage: thumbnailImage)
            return UIImage(cgImage: thumbnailImage)
        } catch let error {
            print(error)
        }

        return nil
    }

    @IBAction func btnFullScreenClicked(_ sender: UIButton) {
        playVideo(needFullScreen:true)
    }

    @IBAction func playAction(_ sender: UIButton) {
        sender.isHidden = true
        playVideo()
//        startLoadingView()
    }

    func playVideo(needFullScreen:Bool=false){
        self.thumbnailImage.isHidden = true
        self.playVideoButton.isHidden = true
        self.btnFullScreenVideo.isHidden = false
        if needFullScreen == false{
            player?.play()
        }else{
            let playerViewController = AVPlayerViewController()
            playerViewController.player = player
            self.present(playerViewController, animated: true) {
                playerViewController.player!.play()
            }
        }
    }

    // MARK: - Loop video when ended.
    @objc func playerItemDidReachEnd(notification: NSNotification) {
        self.player?.seek(to: kCMTimeZero)
        self.player?.play()
    }

    // MARK: - Loop video when ended.
    @objc func playerDidStarted(notification: NSNotification) {

        //        self.player?.seek(to: kCMTimeZero)
        //        self.player?.play()
    }

    override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if object is AVPlayerItem {
            switch keyPath! {
            case "playbackBufferEmpty":
                startLoadingView()
                print("playbackBufferEmpty")
            case "playbackLikelyToKeepUp":
                stopLoadingiew()
                print("playbackLikelyToKeepUp")
            case "playbackBufferFull":
            stopLoadingiew()
                print("playbackBufferFull")
            default:
                break
            }
        }
    }


    func startLoadingView(){

        activityIndicator.color = Color.theme.value// add your color

        self.videoView.addSubview(activityIndicator)
        activityIndicator.startAnimating()
    }

    func stopLoadingiew(){
        activityIndicator.stopAnimating()
        activityIndicator.removeFromSuperview()
    }
}

2 个答案:

答案 0 :(得分:2)

每次调用# Idea by Ben Voigt in https://stackoverflow.com/questions/32869247/a-container-for-integer-intervals-such-as-rangeset-for-c def sort_condense(ivs): if len(ivs) == 0: return [] if len(ivs) == 1: if ivs[0][0] > ivs[0][1]: return [(ivs[0][1], ivs[0][0])] else: return ivs eps = [] for iv in ivs: ivl = min(iv) ivr = max(iv) eps.append((ivl, False)) eps.append((ivr, True)) eps.sort() ret = [] level = 0 i = 0 while i < len(eps)-1: if not eps[i][1]: level = level+1 if level == 1: left = eps[i][0] else: if level == 1: if not eps[i+1][1] and eps[i+1][0] == eps[i][0]+1: i = i+2 continue right = eps[i][0] ret.append((left, right)) level = level-1 i = i+1 ret.append((left, eps[len(eps)-1][0])) return ret In [1]: sort_condense(l) Out[1]: [(5, 13), (15, 25)] 时,您都会调用showVideoView,这会增加一些KVO观测值:

setUIText

但是,您只能在 addObserver(self, forKeyPath: "actionAtItemEnd", options: [], context: nil) let playerItem = self.player?.currentItem playerItem?.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .new, context: nil) playerItem?.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .new, context: nil) playerItem?.addObserver(self, forKeyPath: "playbackBufferFull", options: .new, context: nil) 中删除它们,这是不平衡的。您必须确保对每个属性仅观察一次,而对观察结果则仅移除一次。

答案 1 :(得分:0)

我听了Rob Napier的回答,做了这两个功能

var isObserversAdded = false

func addObservers(){
    self.isObserversAdded = true
    addObserver(self, forKeyPath: "actionAtItemEnd", options: [], context: nil)
    let playerItem = self.player?.currentItem
    playerItem?.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .new, context: nil)
    playerItem?.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .new, context: nil)
    playerItem?.addObserver(self, forKeyPath: "playbackBufferFull", options: .new, context: nil)
}

func removeObservers(){
    if isObserversAdded {
        let playerItem = self.player?.currentItem
        self.removeObserver(self, forKeyPath: "actionAtItemEnd")
        playerItem?.removeObserver(self, forKeyPath: "playbackBufferEmpty")
        playerItem?.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")
        playerItem?.removeObserver(self, forKeyPath: "playbackBufferFull")
        self.isObserversAdded = false
    }
}

因此,当我想删除观察者时,我将确保已添加它,并在每次调用addObservers()之前都调用removeObservers()