从 Parse Array 中删除项目后 UITableView 崩溃

Posted

技术标签:

【中文标题】从 Parse Array 中删除项目后 UITableView 崩溃【英文标题】:UITableView crashing after deleting an item from Parse Array 【发布时间】:2016-02-23 00:11:14 【问题描述】:

我有一个使用 Parse 作为后端的 ios 应用程序。我有一个“用户”类,它有一个数组“myBooks”,其中包含指向“Books”类中的对象的指针。

我有两个视图控制器(vc1、vc2)来显示用户的所有书籍。 vc1按照createdAt降序加载每个用户的书。 Vc2 特定于 PFUser.currentUser,因此它是仅存在的每个单独用户的书籍列表(即,vc1 总共有 30 本书,vc2 有来自当前登录用户的 8 本书)。

手头的问题: 我已经实现了一个滑动删除功能,用户可以从数据库中删除他们的书籍,并且成功完成。新的书籍数组被更新并保存到当前用户的数据库中,删除的书籍不再存在。当我回到 vc1(我正在使用标签栏控制器)时,我有一个 viewDidAppear 函数试图从数据库中重新加载数据并重新加载 tableview。不幸的是,当向下滚动 tableview 时,应用程序总是因越界错误而崩溃。但是,我在vc1中有一个刷新按钮,如果我在回到vc1后按1-2次,就可以避免崩溃。

如何让 bookArray 从最初返回到 vc1 时实际加载和更新 tableview?

viewDidAppear 函数:

override func viewDidAppear(animated: Bool) 

    if PFUser.currentUser() == nil

        self.loginVC = self.storyboard!.instantiateViewControllerWithIdentifier("login") as! LoginViewController
        self.presentViewController(self.loginVC, animated: true, completion: nil)

    


    else
        PFUser.currentUser()!.fetchInBackgroundWithBlock( (object, error) -> Void in
            if error == nil
                //do nothing
            
            else
                let errorCode = error!.code
                switch errorCode
                case 209:
                    PFUser.logOut()
                    self.loginVC = self.storyboard!.instantiateViewControllerWithIdentifier("login") as! LoginViewController
                    self.presentViewController(self.loginVC, animated: true, completion: nil)
                default:
                    break
                

            
        )
    
    self.loadData()


加载数据函数:

func loadData()
        bookListing.removeAll()


        let checkForNewBookData:PFQuery = PFQuery(className: "Book")
        checkForNewBookData.orderByDescending("createdAt")


        checkForNewBookData.findObjectsInBackground().continueWithSuccessBlock( //sets up local data store correctly
            (task: BFTask!) -> AnyObject! in
            return PFObject.unpinAllObjectsInBackgroundWithName("LocalBooks").continueWithSuccessBlock(
                (ignored: BFTask!) -> AnyObject! in

                // Cache new results
                let books = task.result as! [PFObject]
                self.bookListing.removeAll()
                self.bookListing = books
                let userDefaults = NSUserDefaults.standardUserDefaults()
                userDefaults.setObject("true", forKey: "localData")
                return PFObject.pinAllInBackground(books as [PFObject], withName: "LocalCabs")
            )
        )


        let getBookData = PFQuery(className: "Book")
        getBookData.orderByDescending("createdAt")
        getBookData.fromLocalDatastore()

        getBookData.findObjectsInBackgroundWithBlock  (objects : [PFObject]?, error : NSError?) -> Void in
            if error == nil
                if let objects = objects
                    for object in objects
                        self.bookListing.append(object)
                    
                    self.tableView.reloadData()
                
            
            else
                let alert = UIAlertController(title: "Oops!", message: "Something went wrong. Try again.", preferredStyle: UIAlertControllerStyle.Alert)
                alert.addAction(UIAlertAction(title: "Okay", style: UIAlertActionStyle.Default, handler: nil))
                self.presentViewController(alert, animated: true, completion: nil)
            
        

    

【问题讨论】:

【参考方案1】:

您是否真的运行self.tableView.reloadData()您从服务器接收数据之后?乍一看,似乎 reloadData() 首先被调用,然后你的完成块更新你的本地数据。

如果是这样,那么您的数组肯定与您的表数据源不同步。

【讨论】:

【参考方案2】:

您使用此请求的reloadData 部分得到奇怪结果的原因似乎是您在后台队列上调用reloadData,这将使其在不可预测的时间发生。要解决此问题,您可以将调用的那部分放回主队列,如下所示:

NSOperationQueue.mainQueue().addOperationWithBlock  () -> Void in
        self.tableView.reloadData()
    ]

这至少应该可以解决您认为应该在几秒钟后重新加载表格时遇到的问题。不过,我不得不说,我不完全理解为什么在第一次函数调用之后强制用户注销并重新登录。我还认为您可能会遇到两个异步调用都修改相同属性self.bookListing 的潜在问题。您可能需要考虑将第二个查询放入一个单独的函数中,并在第一个函数的完成中调用它。这样您就可以保证第一个查询在第二个查询之前完成。

最后,与tableView.reloadData() 问题类似,从后台队列中显示UIAlertController 也会遇到同样的问题。

【讨论】:

viewDidAppear 中的注销是为了完全不同的东西;当用户更改密码时,他们的会话令牌无效,因此我强制注销以获取新的会话令牌。我会落实你的建议,希望他们能解决,谢谢你的建议!

以上是关于从 Parse Array 中删除项目后 UITableView 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

Parse.Promise 已从类型定义中弃用/删除?

如何通过 PFQuery 从 Parse 中删除对象

使用 Parse Cloud Code 从数组中删除对象

Parse.Promise已被弃用/从类型定义中删除?

调用 array.removeAll() 后 tableview.reload 不起作用 swift 5

使用 canEditRowAtIndexPath 从解析中删除图像