索引 0 超出空数组的界限,有时有效,有时无效

Posted

技术标签:

【中文标题】索引 0 超出空数组的界限,有时有效,有时无效【英文标题】:index 0 beyond bounds for empty array, sometime works, sometime does not 【发布时间】:2015-10-22 19:36:27 【问题描述】:

MainTVC 通过从 parse.com 检索 PFObjects 在 tableView 中显示帖子的图片和标题,如果我点击 tableView 单元之一,它将显示包含图片和文本的详细帖子视图。 mainTVC 使用 prepareForSegue 将数据传递给 detailView。

大多数时候,它运行良好,但是当我尝试在 tableView 显示图片后立即点击它或者我滚动速度非常快时,我得到一个错误,尤其是当我在网速慢的情况下这样做时。

2015-10-22 14:36:52.501 bany[2595:874380] * 由于未捕获的异常 'NSRangeException' 导致应用程序终止,原因:'* -[__NSArrayM objectAtIndex:]: index 0超出空数组的范围'

2015-10-22 14:39:40.855 bany[2600:875293] * 由于未捕获的异常“NSRangeException”而终止应用程序,原因:“* -[__NSArrayM objectAtIndex:]:索引 2超出空数组的范围'

有时我得到索引 2 超出空数组的范围,有时为零。

我不认为我有一个空的 indexPath。

我猜,我在检索数据完成之前点击它。

这是我的 MainTVC 代码

import UIKit
import Parse

class MainTVC: UITableViewController 

@IBOutlet weak var categorySegment: UISegmentedControl!

var resultSearchController : UISearchController!



lazy var postsArray : NSMutableArray = NSMutableArray()
lazy var filterdArray : NSMutableArray = NSMutableArray()
var objectArray = [String]()
  var parentObjectID = String()

var objectTwo : PFObject!
   override func viewDidAppear(animated: Bool) 
    super.viewDidAppear(true)

        //사이즈 조절

        //tableView.estimatedRowHeight = tableView.rowHeight
    //tableView.rowHeight = UITableViewAutomaticDimension


    bringAllDatafromParse()



@IBAction func segmentTapped(sender: AnyObject) 




    postsArray = []



    switch categorySegment.selectedSegmentIndex 
    case 0 :
        bringAllDatafromParse()
    case 1 :
        bringCategoryDataFromParse(1)

    case 2 :
        bringCategoryDataFromParse(2)

    case 3 :
        bringCategoryDataFromParse(3)

    default :
        bringAllDatafromParse()



    
    self.tableView.reloadData()

        


    override func didReceiveMemoryWarning() 
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.


// MARK: - Table view data source

override func numberOfSectionsInTableView(tableView: UITableView) -> Int 
    // #warning Incomplete implementation, return the number of sections
    return 1


override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 

    return postsArray.count








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





       var postObjects = self.postsArray.objectAtIndex(indexPath.row) as! PFObject




    cell.didRequestToShowComment =  (cell) in
        let indexPath = tableView.indexPathForCell(cell)
        let objectToSend = self.postsArray[indexPath!.row] as? PFObject
        // Show your Comment view controller here, and set object to send here
        self.objectTwo = objectToSend!
        self.performSegueWithIdentifier("mainToComment", sender: self)


    
    cell.soldLabel.hidden = true

    if (postObjects.objectForKey("sold") as! Bool) == true 
        cell.soldLabel.hidden = false

    

            //제목

    cell.titleLabel.text = postObjects.objectForKey("titleText") as! String

            // 닉네임

    if let nickNameExists = postObjects.objectForKey("nickName") as? String 
        cell.nickNameLabel.text = nickNameExists
    else 
        cell.nickNameLabel.text = postObjects.objectForKey("username") as? String
    



    //시간
    let dateFormatter:NSDateFormatter = NSDateFormatter()
    dateFormatter.dateFormat = "MM /dd /yy"
    cell.timeLabel.text = (dateFormatter.stringFromDate(postObjects.createdAt!))



            // 가격

    let price = (postObjects.objectForKey("priceText") as! String)
            cell.priceLable.text = "   $\(price)"



               // 이미지
    let mainImages = postObjects.objectForKey("front_image") as! PFFile


    mainImages.getDataInBackgroundWithBlock  (imageData, error) -> Void in
        let image = UIImage(data: imageData!)
        cell.mainPhoto.image = image
    

    // 프로필
    if let profileImages = (postObjects.objectForKey("profile_picture") as? PFFile)
                profileImages.getDataInBackgroundWithBlock  (imageData, error) -> Void in
                    let image = UIImage(data: imageData!)
                    cell.profilePhoto.image = image

        

    else cell.profilePhoto.image = UIImage(named: "AvatarPlaceholder")
    
    circularImage(cell.profilePhoto)

    return cell



    // MARK: - Animate Table View Cell

//    override func tableView(tableView: UITableView, willDisplayCell                cell:   UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) 
//        cell.alpha = 0
        //        UIView.animateWithDuration(0.5)  () -> Void in
        //          //            cell.alpha = 1
//        
//    
//


func bringAllDatafromParse() 
    //activityIndicatorOn()

    postsArray = []
    let query = PFQuery(className: "Posts")

    query.orderByAscending("createdAt")
    query.findObjectsInBackgroundWithBlock  (objects: [PFObject]?, error) -> Void in
        if error == nil && objects != nil

            for object : PFObject in objects! 

                self.postsArray.addObject(object)

            

            let array : Array = self.postsArray.reverseObjectEnumerator().allObjects


            self.postsArray = array as! NSMutableArray


        
     self.tableView.reloadData()

        







func bringCategoryDataFromParse(category : Int) 



    let query = PFQuery(className: "Posts")
     query.whereKey("category", equalTo: category)
    query.orderByAscending("createdAt")
    query.findObjectsInBackgroundWithBlock  (objects: [PFObject]?, error) -> Void in
        if error == nil && objects != nil

            for object : PFObject in objects! 

                self.postsArray.addObject(object)

            

            let array : Array = self.postsArray.reverseObjectEnumerator().allObjects


            self.postsArray = array as! NSMutableArray
            self.tableView.reloadData()

        


    






 override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) 

 if (segue.identifier == "mainToComment") 


let destViewController : CommentVC = segue.destinationViewController as! CommentVC

let selectedRowIndex = self.tableView.indexPathForSelectedRow

destViewController.object = objectTwo




if (segue.identifier == "mainToDetail") 


    let selectedRowIndex = self.tableView.indexPathForSelectedRow
            let destViewController : DetailVC = segue.destinationViewController as! DetailVC
            destViewController.object = (postsArray[(selectedRowIndex?.row)!] as? PFObject)






func circularImage(image : UIImageView) 
    image.layer.cornerRadius = image.frame.size.width / 2
    image.clipsToBounds  = true
    image.layer.borderColor  = UIColor.blackColor().CGColor
    image.layer.borderWidth = 1



我的问题是

如果我的猜测是正确的,我应该如何处理我的代码以等待从解析中检索数据?

如果不是,这个错误的原因是什么?我该如何解决?

【问题讨论】:

【参考方案1】:

您可能需要提供更多代码和详细信息,但有两件事会浮现在脑海中。

首先,根据函数的名称,您似乎正在向另一个后台队列中的数组添加内容,同时尝试通过 UI(您的表格视图)在主队列上表示或与它们交互。我不确定 Parse 是如何工作的,但乍一看,我会确保您没有多个后台查询实例同时使用您的数组。

其次,我不清楚您是如何处理单元重用的。在重用之前,您的单元格是否会尝试利用之前用于表示另一行的信息?也许您没有完全重置所有状态。

评论中的问题更新

查看 Parse(我从未使用过),我发现您并没有正确使用它。 This example 显示(在 Objective-C 中)应该如何处理您对 query.findObjectsInBackgroundWithBlock(...) 的调用。该示例似乎并未演示如何处理 UI 更新。

块的内容正在后台运行。谷歌“可可 UI 线程安全”并尽可能多地阅读。它也适用于更一般的线程安全问题。我认为每次您的 UI 创建表格单元格或与它们交互时,数组都会包含不同的内容(这会影响计数)。您还在后台做了大量的postsArray 替换工作,所有这些都会“绊倒”您的表格视图,因为除了主队列/线程之外,UI 对象通常无法更新或操作。

在您的情况下似乎正在发生的事情是查询正在后台更改数组,同时用户界面(表格视图/单元格等)正在尝试更新。如果我告诉你“剥掉这五个土豆中的每一个”,然后在你还在剥第三个土豆的时候把它们全部倒掉,当你伸手去拿第四个土豆的(现在是空的垃圾箱)时,它不在那里,所以(愚蠢的土豆- 剥皮计算机程序),你不知道该怎么做并说“但是没有第四个土豆”(“potatoes[3] 越界”),或者更糟糕的是,你抓住并剥掉空气,或者你自己的手……你明白我的意思吗?

我的建议是查看示例以及您可以在网上找到的任何其他示例,以使用 findObjectsInBackgroundWithBlock(...) 了解其他人如何处理 UI 更新。我自己没有深入研究,我认为你的块的内容应该处理向可变数组添加东西在主线程并要求表视图更新。为此,也许一旦您确定查询已完成,在后台块中,“跳回”主队列以修改您的 postsArray 并像这样更新表:

// Inside the background block/closure, we've decided the query is complete,
// so we "hop to" the main thread to update our array and ask the UI to reflect it
dispatch_async(dispatch_get_main_queue())  () -> Void in

    self.postsArray.appendContentsOf(objects!)
    self.tableView.reloadData()


这里的想法是,当我们的dispatch_async() 调用中的调度块/闭包被执行时,UI 肯定会从上次扰乱它的任何东西更新。这个小代码块被添加到主队列中,这意味着它 - 并且只有它 - 将在“运行循环之旅”期间执行。这可确保您的帖子数组的任何更改和后续的 UI 更新都将一起发生(正如他们应该发生的那样)并且一次只发生一个

您也应该将此逻辑/方法应用于其他查询,因为每个查询都可能导致相同的问题。我希望这会有所帮助。

【讨论】:

第一个问题中的“另一个后台队列”是什么意思? 你能详细解释一下第二个吗?我怎样才能让我的手机使用以前的信息?哇..这是我的第一个应用程序,需要2个月才能到达这里,这是我绝望的时刻。 我的理解是我应该检索一次数据并使用检索到的数据更新 tableview(UI),而不是每次都尝试检索数据。而且我必须使用 dispatch_async() 它将异步执行一次并且只执行一次。所以我需要在我检索数据的时候放这个。这样对吗?无论如何,感谢您的友好解释,它对我很有帮助,我从这篇文章中学到了很多东西。我希望有一天我可以帮助这样的人。我很快就会到那里再次谢谢你

以上是关于索引 0 超出空数组的界限,有时有效,有时无效的主要内容,如果未能解决你的问题,请参考以下文章

sql索引数组超出界限怎么解决

索引超出了数组界限(Microsoft.SqlServer.Smo)

索引超出了数组界限(Microsoft.SqlServer.Smo)

[Java中的“数组索引超出界限”是什么意思? [重复]

数组索引超出界限项目欧拉问题17

-[__NSArrayM objectAtIndex:]: 索引 11 超出界限 [0 .. 10]' 目标 c