我在第一次启动该应用程序时遇到了崩溃
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
我该如何解决此崩溃?
更新: 我认为这与我的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()
}
}
答案 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()