【问题标题】:storyboard crashes when loading another storyboard from a button从按钮加载另一个故事板时故事板崩溃
【发布时间】:2021-01-25 20:58:36
【问题描述】:
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    hideActivity()
    let products = response.products
    if products.count > 0{
        let controller = HAPurchaseViewController.instantiate() // crashes here
        controller.categories = dataManager.paidCategories()
        controller.products = products
        self.navigationController?.pushViewController(controller, animated: true)
    }
    else{
        DispatchQueue.main.async { [weak self] in
            let alertController = UIAlertController(title: "Oops!", message: "Unable to load or products not found, please try again later", preferredStyle: .alert)
            let okAction = UIAlertAction(title: "Ok", style: .default)
            alertController.addAction(okAction)
            self?.present(alertController, animated: true)
        }
    }
}

除了我使用它来加载我的应用内购买视图控制器时,这个扩展在其他任何地方都可以使用。

import Foundation
import UIKit

protocol Storyboarded {
    static func instantiate() -> Self
}


extension Storyboarded where Self: UIViewController {
    static func instantiate() -> Self {
        let id = String(describing: self)
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
        if #available(iOS 13.0, *) {
            return storyboard.instantiateViewController(identifier: id) as! Self // App crash here when loading storyboard
        } else {
            return storyboard.instantiateViewController(withIdentifier: id) as! Self
        }
    }
    static func value() -> Self {
        return UIViewController() as! Self
    }
}


================================================================= 
Main Thread Checker: UI API called on a background thread: -[UIViewController initWithCoder:] > PID: 13525, TID: 4347723, Thread name: (none), Queue name: com.apple.root.default-qos, QoS: 0
Backtrace:

根据 SO 的建议,我更新了代码

extension Storyboarded where Self: UIViewController {
    static func instantiate() -> Self {
        DispatchQueue.main.async {
            let id = String(describing: self)
            let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
            if #available(iOS 13.0, *) {
                return storyboard.instantiateViewController(identifier: id) as! Self
            } else {
                return storyboard.instantiateViewController(withIdentifier: id) as! Self
            }
        }
    }
    static func value() -> Self {
        return UIViewController() as! Self
    }
}

【问题讨论】:

  • UI API called on a background thread,这很明确。在哪里调用 let controller = HAPurchaseViewController.instantiate() 放在 DispatchQueue.main.async {} 中。
  • 是否有带有该标识符的情节提要或该类是否以编程方式定义?如果是这样,您肯定会遇到问题。
  • 问题是您错误地调用了Storyboarded.instantiate() — 即,来自后台线程。但是您没有显示该代码,因此无法多说。同时,正如您被告知的那样,错误是完全不言自明的,不应该出现任何问题。
  • @matt 用代码更新了问题并发布了新的错误图片

标签: swift model-view-controller storyboard


【解决方案1】:

问题出在这里:

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
    // ... this is a background thread!
}

所以此时你不能在不进入主线程的情况下做任何涉及接口的事情。实际上,您已经在 second 部分这样做了:

hideActivity()
let products = response.products
if products.count > 0{
    let controller = HAPurchaseViewController.instantiate() 
    controller.categories = dataManager.paidCategories()
    controller.products = products
    self.navigationController?.pushViewController(controller, animated: true)
}
else{
    // LOOK! You are already doing it here
    DispatchQueue.main.async { [weak self] in
        let alertController = UIAlertController(title: "Oops!", message: "Unable to load or products not found, please try again later", preferredStyle: .alert)
        let okAction = UIAlertAction(title: "Ok", style: .default)
        alertController.addAction(okAction)
        self?.present(alertController, animated: true)
    }
}

嗯?所以在第一部分做同样的事情!

hideActivity()
let products = response.products
if products.count > 0{
    DispatchQueue.main.async { [weak self] in
        let controller = HAPurchaseViewController.instantiate() 
        controller.categories = dataManager.paidCategories()
        controller.products = products
        self?.navigationController?.pushViewController(controller, animated: true)
    }
} else {
    DispatchQueue.main.async { [weak self] in
        let alertController = UIAlertController(title: "Oops!", message: "Unable to load or products not found, please try again later", preferredStyle: .alert)
        let okAction = UIAlertAction(title: "Ok", style: .default)
        alertController.addAction(okAction)
        self?.present(alertController, animated: true)
    }
}

【讨论】:

    猜你喜欢
    • 2013-09-17
    • 2017-01-14
    • 2014-11-13
    • 2015-10-26
    • 2012-09-22
    • 2017-02-22
    • 2016-05-04
    • 1970-01-01
    • 2013-02-03
    相关资源
    最近更新 更多