2017-05-29 65 views
3

我负责一个完整的斯威夫特3应用程序和经常发生的事故之一是SIGBUS信号,我无法理解在所有:斯威夫特3个协议面向随机SIGBUS编程结果崩溃

Thread 0 Crashed: 
0 libswiftCore.dylib 0x00000001009b4ac8 0x1007b8000 +2083528 
1 LeadingBoards   @objc PageView.prepareForReuse() ->() (in LeadingBoards) (PageView.swift:0) +1114196 
2 LeadingBoards   specialized ReusableContentView<A where ...>.reuseOrInsertView(first : Int, last : Int) ->() (in LeadingBoards) (ReusableView.swift:101) +1730152 
3 LeadingBoards   DocumentViewerViewController.reuseOrInsertPages() ->() (in LeadingBoards) (DocumentViewerViewController.swift:0) +1036080 
4 LeadingBoards   specialized DocumentViewerViewController.scrollViewDidScroll(UIScrollView) ->() (in LeadingBoards) (DocumentViewerViewController.swift:652) +1089744 
5 LeadingBoards   @objc DocumentViewerViewController.scrollViewDidScroll(UIScrollView) ->() (in LeadingBoards) +1028252 
6 UIKit     0x000000018c2a68d4 0x18bf85000 +3283156 
7 UIKit     0x000000018bfb2c08 0x18bf85000 +187400 
8 UIKit     0x000000018c143e5c 0x18bf85000 +1830492 
9 UIKit     0x000000018c143b4c 0x18bf85000 +1829708 
10 QuartzCore   0x00000001890755dc 0x18906b000 +42460 
11 QuartzCore   0x000000018907548c 0x18906b000 +42124 
12 IOKit     0x00000001860d7b9c 0x1860d2000 +23452 
13 CoreFoundation  0x0000000185e01960 0x185d3e000 +801120 
14 CoreFoundation  0x0000000185e19ae4 0x185d3e000 +899812 
15 CoreFoundation  0x0000000185e19284 0x185d3e000 +897668 
16 CoreFoundation  0x0000000185e16d98 0x185d3e000 +888216 
17 CoreFoundation  0x0000000185d46da4 0x185d3e000 +36260 
18 GraphicsServices  0x00000001877b0074 0x1877a4000 +49268 
19 UIKit     0x000000018bffa058 0x18bf85000 +479320 
20 LeadingBoards   main (in LeadingBoards) (AppDelegate.swift:13) +77204 
21 libdyld.dylib   0x0000000184d5559c 0x184d51000 +17820 

其背后的逻辑是在一个滚动视图中重复使用视图的逻辑,如Apple在WWDC视频中所描述的(无法找到年份和视频...):

PageView是一个实现ReusableView和Indexed :

class PageView: UIView { 

    enum Errors: Error { 
     case badConfiguration 
     case noImage 
    } 

    enum Resolution: String { 
     case high 
     case low 

     static var emptyGeneratingTracker: [PageView.Resolution: Set<String>] { 
      return [.high:Set(), 
        .low:Set()] 
     } 

     /// SHOULD NOT BE 0 
     var quality: CGFloat { 
      switch self { 
      case .high: 
       return 1 
      case .low: 
       return 0.3 
      } 
     } 

     var JPEGQuality: CGFloat { 
      switch self { 
      case .high: 
       return 0.8 
      case .low: 
       return 0.25 
      } 
     } 

     var atomicWrite: Bool { 
      switch self { 
      case .high: 
       return false 
      case .low: 
       return true 
      } 
     } 

     var interpolationQuality: CGInterpolationQuality { 
      switch self { 
      case .high: 
       return .high 
      case .low: 
       return .low 
      } 
     } 

     var dispatchQueue: OperationQueue { 
      switch self { 
      case .high: 
       return DocumentBridge.highResOperationQueue 
      case .low: 
       return DocumentBridge.lowResOperationQueue 
      } 
     } 
    } 

    @IBOutlet weak var imageView: UIImageView! 

    // Loading 
    @IBOutlet weak var loadingStackView: UIStackView! 
    @IBOutlet weak var pageNumberLabel: UILabel! 

    // Error 
    @IBOutlet weak var errorStackView: UIStackView! 

    // Zoom 
    @IBOutlet weak var zoomView: PageZoomView! 

    fileprivate weak var bridge: DocumentBridge? 

    var displaying: Resolution? 
    var pageNumber = 0 

    override func layoutSubviews() { 
     super.layoutSubviews() 

     refreshImageIfNeeded() 
    } 

    func configure(_ pageNumber: Int, zooming: Bool, bridge: DocumentBridge) throws { 
     if pageNumber > 0 && pageNumber <= bridge.numberOfPages { 
      self.bridge = bridge 
      self.pageNumber = pageNumber 
      self.zoomView.configure(bridge: bridge, pageNumber: pageNumber) 
     } else { 
      throw Errors.badConfiguration 
     } 

     NotificationCenter.default.addObserver(self, selector: #selector(self.pageRendered(_:)), name: .pageRendered, object: bridge) 
     NotificationCenter.default.addObserver(self, selector: #selector(self.pageFailedRendering(_:)), name: .pageFailedRendering, object: bridge) 

     pageNumberLabel.text = "PAGE".localized + " \(pageNumber)" 

     if displaying == nil { 
      loadingStackView.isHidden = false 
      errorStackView.isHidden = true 
     } 
     if displaying != .high { 
      refreshImage() 
     } 

     if zooming { 
      startZooming() 
     } else { 
      stopZooming() 
     } 
    } 

    fileprivate func isNotificationRelated(notification: Notification) -> Bool { 
     guard let userInfo = notification.userInfo else { 
      return false 
     } 

     guard pageNumber == userInfo[DocumentBridge.PageNotificationKey.PageNumber.rawValue] as? Int else { 
      return false 
     } 

     guard Int(round(bounds.width)) == userInfo[DocumentBridge.PageNotificationKey.Width.rawValue] as? Int else { 
      return false 
     } 

     guard userInfo[DocumentBridge.PageNotificationKey.Notes.rawValue] as? Bool == false else { 
      return false 
     } 

     return true 
    } 

    func pageRendered(_ notification: Notification) { 
     guard isNotificationRelated(notification: notification) else { 
      return 
     } 

     if displaying == nil || (displaying == .low && notification.userInfo?[DocumentBridge.PageNotificationKey.Resolution.rawValue] as? String == Resolution.high.rawValue) { 
      refreshImage() 
     } 
    } 

    func pageFailedRendering(_ notification: Notification) { 
     guard isNotificationRelated(notification: notification) else { 
      return 
     } 

     if displaying == nil { 
      imageView.image = nil 
      loadingStackView.isHidden = true 
      errorStackView.isHidden = false 
     } 
    } 

    func refreshImageIfNeeded() { 
     if displaying != .high { 
      refreshImage() 
     } 
    } 

    fileprivate func refreshImage() { 
     let pageNumber = self.pageNumber 
     let width = Int(round(bounds.width)) 
     DispatchQueue.global(qos: .userInitiated).async(execute: { [weak self]() in 
      do { 
       try self?.setImage(pageNumber, width: width, resolution: .high) 
      } catch { 
       _ = try? self?.setImage(pageNumber, width: width, resolution: .low) 
      } 
     }) 
    } 

    func setImage(_ pageNumber: Int, width: Int, resolution: Resolution) throws { 
     if let image = try self.bridge?.getImage(page: pageNumber, width: width, resolution: resolution) { 
      DispatchQueue.main.async(execute: { [weak self]() in 
       if pageNumber == self?.pageNumber { 
        self?.imageView?.image = image 
        self?.displaying = resolution 
        self?.loadingStackView.isHidden = true 
        self?.errorStackView.isHidden = true 
       } 
      }) 
     } else { 
      throw Errors.noImage 
     } 
    } 
} 

extension PageView: ReusableView, Indexed { 
    static func instanciate() -> PageView { 
     return UINib(nibName: "PageView", bundle: nil).instantiate(withOwner: nil, options: nil).first as! PageView 
    } 

    var index: Int { 
     return pageNumber 
    } 

    func hasBeenAddedToSuperview() { } 

    func willBeRemovedFromSuperview() { } 

    func prepareForReuse() { 
     NotificationCenter.default.removeObserver(self, name: .pageRendered, object: nil) 
     NotificationCenter.default.removeObserver(self, name: .pageFailedRendering, object: nil) 

     bridge = nil 
     imageView?.image = nil 
     displaying = nil 
     pageNumber = 0 
     zoomView?.prepareForReuse() 
    } 

    func prepareForRelease() { } 
} 

// MARK: - Zoom 
extension PageView { 
    func startZooming() { 
     bringSubview(toFront: zoomView) 
     zoomView.isHidden = false 
     setNeedsDisplay() 
    } 

    func stopZooming() { 
     zoomView.isHidden = true 
    } 
} 

其中ReusableView和索引被定义协议这种方式:

protocol Indexed { 
    var index: Int { get } 
} 

protocol ReusableView { 
    associatedtype A 

    static func instanciate() -> A 

    func hasBeenAddedToSuperview() 
    func willBeRemovedFromSuperview() 
    func prepareForReuse() 
    func prepareForRelease() 
} 

// Make some func optionals 
extension ReusableView { 
    func hasBeenAddedToSuperview() {} 
    func willBeRemovedFromSuperview() {} 
    func prepareForReuse() {} 
    func prepareForRelease() {} 
} 

ReusableContentView是管理被插入,或再利用的视图的视图。它是根据实现的含有视图类型:

class ReusableContentView<T: ReusableView>: UIView where T: UIView { 
    var visible = Set<T>() 
    var reusable = Set<T>() 

    ... 
} 

extension ReusableContentView where T: Indexed { 
    /// To insert view using a range of ids 
    func reuseOrInsertView(first: Int, last: Int) { 
     // Removing no longer needed views 
     for view in visible { 
      if view.index < first || view.index > last { 
       reusable.insert(view) 
       view.willBeRemovedFromSuperview() 
       view.removeFromSuperview() 
       view.prepareForReuse() 
      } 
     } 
     // Removing reusable pages from visible pages array 
     visible.subtract(reusable) 

     // Add the missing views 
     for index in first...last { 
      if !visible.map({ $0.index }).contains(index) { 
       let view = dequeueReusableView() ?? T.instanciate() as! T // Getting a new page, dequeued or initialized 
       if configureViewWithIndex?(view, index) == true { 
        addSubview(view) 
        view.hasBeenAddedToSuperview() 
        visible.insert(view) 
       } 
      } 
     } 
    } 
} 

女巫被称为DocumentViewerViewController.reuseOrInsertPages()scrollviewDidScroll代表触发。

这里有什么可以挑拨我的SIGBUS信号?这是我用来使协议功能可选的func prepareForReuse() {}的默认实现吗?任何其他想法?

当然,这个崩溃是完全随机的,我无法重现它。我刚刚从应用程序的prod版本收到关于它的崩溃报告。 感谢您的帮助!

回答

2

对我来说,它看起来像在PageView.prepareForReuse()中出了问题。我不知道的属性,但是从prepareForReuse功能,它看起来像您正在访问特性,这也许是@IBOutlets:

bridge = nil 
imageView.image = nil 
displaying = nil 
pageNumber = 0 
zoomView.prepareForReuse() 

难道imageViewzoomView是零,当您试图访问它们?如果是这样,这可能是最简单的解决办法:

func prepareForReuse() { 
    NotificationCenter.default.removeObserver(self, name: .pageRendered, object: nil) 
    NotificationCenter.default.removeObserver(self, name: .pageFailedRendering, object: nil) 

    bridge = nil 
    imageView?.image = nil 
    displaying = nil 
    pageNumber = 0 
    zoomView?.prepareForReuse() 
} 

同样,我不知道你的网页浏览的实现细节,我只是猜测,是因为它看起来像你从笔尖,因此将其实例化我的猜测是你正在使用例如@IBOutlet weak var imageView: UIImageView!

如果由于某种原因这个imageView变成零,访问它会导致你的应用程序崩溃。

+0

事实上,imageView和zoomView是IBOutlets的一个强大的解开,因为该应用程序的几个版本。但是,这个崩溃发生在应用程序以前的版本,其中视图不是IBOutlets。 另外:如果它的确是零;反而是SIGTRAP? 无论如何,我将在代码中添加那些安全的解包,并看看接下来会发生什么。 – Dean

+0

好问题。根据这个问题https://stackoverflow.com/questions/7446655/exception-types-in-ios-crash-logs SIGBUS的意思是“访问无效的内存地址。”而SIGTRAP的意思是“调试器相关”。 :/你一定要检查这个问题和所有的答案 – xxtesaxx

+0

虽然我不知道我的问题是否完全解决,你完全应该得到这个答案的完整赏金。我会稍后再回来告诉你,如果崩溃从我们的产品日志中消失了! – Dean