TabBarController: Going Tab-To-Tab - fatal error: Array index out of range
TabBarController: Going Tab-To-Tab - fatal error: Array index out of range
我有一个带有两个选项卡的 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
那样展开图像。
你这里有一个基本的设计错误。您正在尝试获取单元格的数据并将其设置为该块。但是单元格对象会被重新使用,一旦 table 视图离开屏幕,所有单元格都可能与 table 解除关联。你需要做的是获取数据,填充你的模型(即数组或字典),然后 post 到主线程的一个块,需要刷新这样一行。如果显示该行,您可以从 table 视图(通过可见单元格数组)中获取它。如果它没有显示,什么也不做,当您被要求输入单元格时,您将有数据来填充它。
我在我的应用程序中所做的是有一个单独的方法来获取单元格、它的索引并更新单元格内容。当我被要求一个新的单元格时,我分配一个,然后调用这个方法。当我收到某种关于可见单元格信息可能已更改的内部通知时,我会调用相同的方法(重点是将写入单元格内容的代码放在一处,而不是两处)。
编辑:同样,您的问题在这里:
(imageData: NSData?, error: NSError?) -> Void in
if !(error != nil) {
cell.itemImage?.image = UIImage(data: imageData!)
cell.imageView?.contentMode = .ScaleAspectFit
cell.imageView?.clipsToBounds = true
}
您正在保存对 "cell" 的引用,该引用将来对于您要在其中保存的信息无效,并且单元格本身可能已被释放。
当数据从后台进来时,post一个块到主线程,保存在数组或字典中。同样的方法可以更新所有可见的单元格。您不需要 NSNotification 来执行此操作 - post在主线程上返回一个块可以很好地完成此操作。
您的 getAndShowFeedItems 方法有误。服务器请求可能需要更多时间。您正在清理阵列,但不要重新加载 table 视图。如果在获取项目之前滚动 table 视图,您会发生崩溃。您需要为此更改代码:
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 部分也有错误。滚动时可以设置错误的图像。因为 table 视图将可重用不可见单元格。因此,当您完成下载图像数据单元格时,可能会包含错误的 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
}
代码可能包含一些不准确的地方,我没有编译它。您还需要在 class currentItem 变量中添加新的 属性。
我有一个带有两个选项卡的 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
那样展开图像。
你这里有一个基本的设计错误。您正在尝试获取单元格的数据并将其设置为该块。但是单元格对象会被重新使用,一旦 table 视图离开屏幕,所有单元格都可能与 table 解除关联。你需要做的是获取数据,填充你的模型(即数组或字典),然后 post 到主线程的一个块,需要刷新这样一行。如果显示该行,您可以从 table 视图(通过可见单元格数组)中获取它。如果它没有显示,什么也不做,当您被要求输入单元格时,您将有数据来填充它。
我在我的应用程序中所做的是有一个单独的方法来获取单元格、它的索引并更新单元格内容。当我被要求一个新的单元格时,我分配一个,然后调用这个方法。当我收到某种关于可见单元格信息可能已更改的内部通知时,我会调用相同的方法(重点是将写入单元格内容的代码放在一处,而不是两处)。
编辑:同样,您的问题在这里:
(imageData: NSData?, error: NSError?) -> Void in
if !(error != nil) {
cell.itemImage?.image = UIImage(data: imageData!)
cell.imageView?.contentMode = .ScaleAspectFit
cell.imageView?.clipsToBounds = true
}
您正在保存对 "cell" 的引用,该引用将来对于您要在其中保存的信息无效,并且单元格本身可能已被释放。
当数据从后台进来时,post一个块到主线程,保存在数组或字典中。同样的方法可以更新所有可见的单元格。您不需要 NSNotification 来执行此操作 - post在主线程上返回一个块可以很好地完成此操作。
您的 getAndShowFeedItems 方法有误。服务器请求可能需要更多时间。您正在清理阵列,但不要重新加载 table 视图。如果在获取项目之前滚动 table 视图,您会发生崩溃。您需要为此更改代码:
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 部分也有错误。滚动时可以设置错误的图像。因为 table 视图将可重用不可见单元格。因此,当您完成下载图像数据单元格时,可能会包含错误的 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
}
代码可能包含一些不准确的地方,我没有编译它。您还需要在 class currentItem 变量中添加新的 属性。