MainTVC通过从parse.com检索PFObject来显示tableView中的帖子的图片和标题,如果我点击其中一个tableView Cell,它将显示包含图片和文本的详细帖子视图。 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:]:index 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
}
}
我的问题是
如果我的猜测是正确的,我应该怎样处理我的代码,等待从解析中检索数据?
如果不是,这个错误的原因是什么?我该如何解决?
答案 0 :(得分:0)
您可能需要提供更多代码和详细信息,但有两件事情会浮现在脑海中。
首先,根据函数的名称,当您尝试通过UI(表视图)在主队列上表示或与它们交互时,您似乎正在向另一个后台队列中的数组添加内容。我不确定Parse是如何工作的,但一目了然,我确保你不会同时在你的阵列中使用多个后台查询实例。
其次,我不清楚你是如何处理细胞再利用的。在重用之前,您的单元格是否可以尝试使用之前用于表示另一行的信息?也许你并没有完全重置所有州。
评论中的问题更新
看看Parse(我从未使用过),我发现你并没有正确使用它。 This example显示(在Objective-C中)应如何处理您对query.findObjectsInBackgroundWithBlock(...)
的调用。该示例似乎并未演示如何处理更新UI。
块的内容正在后台运行。 Google" cocoa UI线程安全"并尽可能多地阅读。它也适用于更一般的线程安全问题。我认为每次你的UI创建表格单元格或与它们交互时,数组都会保存不同的东西(这会影响计数)。您还在后台更换了postsArray
,并且所有这些都会在#34;绊倒"您的表视图,因为UI对象通常无法从主队列/线程以外的任何更新或操作。
在您的情况下似乎发生的情况是,在用户界面(表视图/单元格等)尝试更新的同时,查询正在后台更改数组。如果我告诉你"剥掉这五个土豆中的每一个"当你进入第三个马铃薯(现在是空箱子)进入第四个马铃薯时,把它们全部丢弃,它不在那里,所以(笨土豆剥皮计算机程序,你是的,你不知道该怎么做,并说'#34;但是没有第四个马铃薯" (" 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肯定会从上次被扰动的任何内容进行更新。这个小代码块被添加到主队列中,这意味着它 - 并且只有它 - 将在执行循环期间执行#34;行程#34;。这样可以确保对posts数组和后续UI更新的任何更改都将在一起(就像他们应该)中进行,并且一次只能一次。
您应该将此逻辑/方法应用于其他查询,因为每个查询都可能导致相同的问题。我希望这会有所帮助。