TabBarController:Go-Tab-To-Tab - 致命错误:数组索引超出范围

时间:2015-09-09 15:47:34

标签: ios swift uitableview parse-platform uitabbarcontroller

我有一个带有两个标签的TabBarController(一个TableViewController,一个CollectionViewController)。当我从一个标签切换到另一个标签时,我的应用程序崩溃了。它总是在UITableViewController选项卡上崩溃,这是一个简单的Feed。显示以下断点和信息:

0x498e44 <+68>:  bl     0x4efb9c                  ; function signature specialization <Arg[0] = Exploded, Arg[1] = Exploded> of Swift.(_fatalErrorMessage (Swift.StaticString, Swift.StaticString, Swift.StaticString, Swift.UInt) -> ()).(closure #2)
->  0x498e48 <+72>:  trap    // **This is the breakpoint

在FeedView中,我有以下内容,并在第3行找到了另一个断点:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("feedCell", forIndexPath: indexPath) as! FeedViewCell
    let currentItem = feedItems[indexPath.row]      // **Other error breakpoint occurs here

    let date = currentItem.createdAt
    let formatter = NSDateFormatter()
    formatter.dateFormat = "hh:mm"
    let dateString = formatter.stringFromDate(date!)

    cell.userName?.text = currentItem.userName
    cell.itemName?.text = currentItem.itemName
    cell.timeStamp?.text = dateString

    // MARK: Setup image for cell
    cell.itemImage?.image = UIImage(named: "1.png")
    let image = currentItem.imageFile
    image.getDataInBackgroundWithBlock { (imageData: NSData?, error: NSError?) -> Void in
        if !(error != nil) {
            cell.itemImage?.image = UIImage(data: imageData!)
            cell.imageView?.contentMode = .ScaleAspectFit
            cell.imageView?.clipsToBounds = true
        }
    }
    return cell
}

func getAndShowFeedItems() {                // I call this in ViewWillAppear

    feedItems.removeAll(keepCapacity: false)

    let getFeedItems = FeedItem.query()
    getFeedItems!.orderByDescending("createdAt")
    getFeedItems!.findObjectsInBackgroundWithBlock { (objects: [AnyObject]?, error: NSError?) -> Void in
        if error == nil {
            for object in objects! {
                self.feedItems.append(object as! FeedItem)
                if self.feedItems.count == 35 {
                    break
                }
            }
        } else if error!.code ==  PFErrorCode.ErrorConnectionFailed.rawValue {
            self.showNetworkAlert()
            print("there's a networking problem")
        }
            self.tableView.reloadData()
            self.refreshControl?.endRefreshing()
            self.resetUserDefaults()
            self.scrollToFirstRow()
    }
}

注意:我使用Parse.com作为我的图像存储,而venueImage是一个PFImage,它不允许我像UIImage那样解开图像。

2 个答案:

答案 0 :(得分:1)

这里有一个基本的设计错误。您正在尝试获取单元格的数据并使用该块进行设置。但是细胞对象会被重复使用,一旦tableview离开屏幕,所有细胞都可能会从表中解除关联。你需要做的是获取数据,填充你的模型(即数组或字典),然后将一个块发布到主线程,这样的行需要刷新。如果显示该行,则可以从tableview(通过可见单元格数组)获取该行。如果它没有显示,什么都不做,当你被要求提供单元格时,你将有数据填充它。

我在我的应用程序中所做的是使用单独的方法来获取单元格,索引以及更新单元格内容。当我被要求新的单元格时,我会分配一个,然后调用此方法。当我得到某种内部通知,可见的单元格信息可能已经改变时,我调用相同的方法(关键是你把代码将内容写入一个地方,而不是两个)。

编辑:再次,你的问题在这里:

(imageData: NSData?, error: NSError?) -> Void in
    if !(error != nil) {
        cell.itemImage?.image = UIImage(data: imageData!)
        cell.imageView?.contentMode = .ScaleAspectFit
        cell.imageView?.clipsToBounds = true
    }

您正在保存对&#34;单元格的引用&#34;将来不会对您要保存的信息有效,并且单元本身可能已被解除分配。

当数据从后台传入时,将块发布到主线程并将其保存在数组或字典中。然后,相同的方法可以更新所有可见单元格。您不需要NSNotification来执行此操作 - 在主线程上发布一个块可以很好地完成这个操作。

答案 1 :(得分:1)

getAndShowFeedItems方法中存在错误。服务器请求可能需要更多时间。您是清理您的数组但不重新加载表视图。如果在获取项目之前滚动表视图,则会崩溃。您需要更改代码:

func getAndShowFeedItems() {                // I call this in ViewWillAppear

    feedItems.removeAll(keepCapacity: false)
    self.tableView.reloadData() // Need reload tableview when change it content
    let getFeedItems = FeedItem.query()
    getFeedItems!.orderByDescending("createdAt")
    getFeedItems!.findObjectsInBackgroundWithBlock { (objects: [AnyObject]?, error: NSError?) -> Void in
        if error == nil {
            for object in objects! {
                self.feedItems.append(object as! FeedItem)
                if self.feedItems.count == 35 {
                    break
                }
            }
        } else if error!.code ==  PFErrorCode.ErrorConnectionFailed.rawValue {
            self.showNetworkAlert()
            print("there's a networking problem")
        }
            self.tableView.reloadData()
            self.refreshControl?.endRefreshing()
            self.resetUserDefaults()
            self.scrollToFirstRow()
    }
}  

你也在cellForRowAtIndexPath部分有错误。滚动时可以设置错误的图像。因为表视图将可重用不可见的单元格。所以当你完成下载图像数据单元格时可能包含错误的indexPath。你可以像这样重做你的代码。

 override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("feedCell", forIndexPath: indexPath) as! FeedViewCell
        let currentItem = feedItems[indexPath.row]      // **Other error breakpoint occurs here

        let date = currentItem.createdAt
        let formatter = NSDateFormatter()
        formatter.dateFormat = "hh:mm"
        let dateString = formatter.stringFromDate(date!)

        cell.userName?.text = currentItem.userName
        cell.itemName?.text = currentItem.itemName
        cell.timeStamp?.text = dateString

        // MARK: Setup image for cell
        if let image = UIImage(data: currentItem.imageData)
        {
            cell.itemImage?.image = image
            cell.imageView?.contentMode = .ScaleAspectFit
            cell.imageView?.clipsToBounds = true
        }else
        {
            cell.itemImage?.image = UIImage(named: "1.png")


            let image = currentItem.imageFile
            image.getDataInBackgroundWithBlock { (imageData: NSData?, error: NSError?) -> Void in
                if !(error != nil) {
                    currentItem.imageData = imageData!
                    let index = feedItems.indexOfObject(currentItem)
                    if index != NSNotFound
                    {
                        tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: 0)], withRowAnimation: .None)
                    }

                }
            }
        }
        return cell
    }

代码可能包含一些我不编译的不准确之处。您还需要在类currentItem变量中添加新属性。