【问题标题】:Refreshing gives Error that "found nil while unwrapping an Optional value"刷新给出“在展开可选值时发现 nil”的错误
【发布时间】:2016-02-26 01:04:39
【问题描述】:

我将这个 getCloudKit 函数从 ViewController.swift 移动到 Lay.swift,这样我就可以将所有内容放在一个类中。

var objects = [Lay]()

override func viewDidLoad() {
    super.viewDidLoad()

    self.refreshControl?.addTarget(self, action: "handleRefresh:", forControlEvents: UIControlEvents.ValueChanged)
    self.getCloudKit()
}

func handleRefresh(refreshControl: UIRefreshControl) {

    self.objects.removeAll()
    self.getCloudKit()
}

func getCloudKit() {

    let now = NSDate()
    let predicate = NSPredicate(format: "TimeDate > %@", now)
    let sort = NSSortDescriptor(key: "TimeDate", ascending: true)

    let container = CKContainer.defaultContainer()
    let publicData = container.publicCloudDatabase

    let query = CKQuery(recordType: “lay”, predicate: predicate)
    query.sortDescriptors = [sort]
    publicData.performQuery(query, inZoneWithID: nil) { results, error in
        if error == nil { 
            for lay in results! {
                let newlay = Lay()

                newLay.tColor = lay["tColor"] as! String
                newLay.timeDate = lay["TimeDate"] as! AnyObject
                newLay.matchup = lay["Matchup"] as! String

                let applicationDict = ["tColor" : newLay.tColor, "Matchup" : newLay.matchup]
                let transfer = WCSession.defaultSession().transferUserInfo(applicationDict)

                self.objects.append(newLay)
            }

            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                self.refreshControl!.endRefreshing()
                self.tableView.reloadData()
            })

        } else {
            print(error)
        }
    }

}

问题是我移动它的时候(以及必要的相关代码):

  • Lay.swift 中的错误 TableViewController().refreshControl!.endRefreshing()“致命错误:在展开时意外发现 nil 可选值”
  • 需要将我的 WCSession: transferUserInfo 代码从 getCloudKit 放入我的 AppDelegate.swift,但尝试时总是出错

ViewController.swift

override func viewDidLoad() {
    super.viewDidLoad()

    self.refreshControl?.addTarget(self, action: "handleRefresh:", forControlEvents: UIControlEvents.ValueChanged)
    Lay().getCloudKit()
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return Lay().objects.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath)

    let object = Lay().objects[indexPath.row];
    if let label = cell.textLabel{
        label.text = object.matchup
    }

    return cell
}

func handleRefresh(refreshControl: UIRefreshControl) {
    Lay().objects.removeAll()
    Lay().getCloudKit()
}

Lay.swift:

  var objects = [Lay]()


func getCloudKit() {

    let now = NSDate()
    let predicate = NSPredicate(format: "TimeDate > %@", now)
    let sort = NSSortDescriptor(key: "TimeDate", ascending: true)

    let container = CKContainer.defaultContainer()
    let publicData = container.publicCloudDatabase

    let query = CKQuery(recordType: “lay”, predicate: predicate)
    query.sortDescriptors = [sort]
    publicData.performQuery(query, inZoneWithID: nil) { results, error in
        if error == nil { 
            for lay in results! {
                let newlay = Lay()

                newLay.tColor = lay["tColor"] as! String
                newLay.timeDate = lay["TimeDate"] as! AnyObject
                newLay.matchup = lay["Matchup"] as! String

                let applicationDict = ["tColor" : newlay.tColor, "Matchup" : newlay.matchup]
                let transfer = WCSession.defaultSession().transferUserInfo(applicationDict)

                self.objects.append(newlay)
            }

            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                TableViewController().refreshControl!.endRefreshing()
                TableViewController().tableView.reloadData()
            })

        } else {
            print(error)
        }
    }

AppDelegate:

private func setupWatchConnectivity() {
    if WCSession.isSupported() {
        let session = WCSession.defaultSession()
        session.delegate = self
        session.activateSession()
    }
}


private func sendUpdatedDataToWatch(notification: NSNotification) {
    if WCSession.isSupported() {
        let session = WCSession.defaultSession()
        if session.watchAppInstalled
        {
                let applicationDict = ["TColor" : Lay().tColor, "Matchup" : Lay().matchup]
                let transfer = WCSession.defaultSession().transferUserInfo(applicationDict)
                NSLog("Transfer AppDelegate: %@", transfer)
                NSLog("Trans AppDelegate: %@", applicationDict)

                session.transferCurrentComplicationUserInfo(applicationDict)
        }
    }
}

【问题讨论】:

  • 每次您执行TableViewController()Lay() 时,它都会创建这些类的新实例,而不是访问其他对象正在使用的现有实例。
  • 啊,所以我的整个事情都搞砸了,或者我需要做一些小的改变,比如做一个 sharedInstance 什么的?
  • 您需要保留对表格视图控制器的引用,可能通过使用 IBOutlet。
  • 好的。在TableViewController 中添加IBOutlet,然后在Lay 中以某种方式引用它?

标签: ios xcode swift uitableview watchkit


【解决方案1】:

您的代码始终包含ViewController()Lay()。这将创建这些对象的新实例。因此,尽管refreshControl 在您的实际视图控制器中不是 nil,但在新创建的视图控制器中它将是 nil。

通过拆分getCloudKit 函数,您允许视图控制器只管理视图,而新类只管理Cloud Kit。这很好,所以理想情况下,您的 Cloud Kit 控制器不应该对视图控制器一无所知。因此,getCloudKit 不应该调用 reloadData。相反,您可以将一个闭包传递给 getCloudKit,它会在查询完成时被调用。大致如下:

    func getCloudKit(completion completionHandler: (([Lay]) -> Void)?) {
         ...
         publicData.performQuery(query, inZoneWithID: nil) { results, error in
            if error == nil { 
                ...
                if let completion = completionHandler {
                    completion(self.objects)
                }
            } else {
                print(error)
         }
    }

然后在 ViewController 中:

    let layCloudKit = LayCloudKit()
    layCloudKit.getCloudKit(completion: { (objects) -> Void in
        dispatch_async(dispatch_get_main_queue(), {
            self.objects = objects
            self.refreshControl!.endRefreshing()
            self.tableView.reloadData()
        })
    })

请注意,我假设您会将 Lay Cloud Kit 控制器放入单独的 Swift 文件中,因为 Lay 模型类不需要了解 Cloud Kit。如果你想把它和Lay放在同一个文件中,那么你应该将func标记为staticclass,因为你不想创建一个Lay的虚拟实例只是为了调用getCloudKit。在这种情况下,您可以使用 Lay.getCloudKit 调用它(即指定 Lay 类,而不是 Lay 实例)。

【讨论】:

  • 去试一试,我会告诉你的,非常感谢!!
  • 现在正在实施,但坚持我认为一定是 Xcode 错误,因为收到错误“使用未解析的标识符 LayCloudKit”这没有任何意义,它不会找到那个文件。 (我已经查看了所有这些步骤,看看我是否做错了什么,也没有任何问题stackoverflow.com/questions/26099111/…
  • 这不太可能是一个错误。您创建了一个名为 LayCloudKit.swift 的新文件,其中包含 class LayCloudKit?
  • 啊,不,我没有做课堂部分,好的,现在重试。
  • 所以我运行了它,所有数据看起来都像它的日志记录,但在 tableview 中什么也没有出现。所以我在主要函数上使用断点运行它,并且表函数在getCloudKit 之前被调用。所以我猜表加载,但没有数据所以它是空的,然后不知何故我没有让表重新加载。所以不知何故,我认为完成处理程序无法调用重新加载?我确定我错过了什么,有什么想法吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-31
  • 2023-03-09
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多