IOS Swift Master Detail Application, managed object, tableView: endUpdates() 调用configureCell() 但崩溃,r

Posted

技术标签:

【中文标题】IOS Swift Master Detail Application, managed object, tableView: endUpdates() 调用configureCell() 但崩溃,reloadData() 不调用【英文标题】:IOS Swift Master Detail Application, managed object, tableView: endUpdates() calls configureCell() but crash, reloadData() does not call it 【发布时间】:2014-12-04 15:59:59 【问题描述】:

我是 Swift 的新手,我尝试制作我的第一个测试应用程序,但我陷入了一个托管对象问题,我认为......知道我做错了什么吗?

我开始使用Master Detail Application模板制作应用程序,并尽可能使用XCode的storyboard修改基本模板。

我修改了 TableView 以使用自定义 TableViewCells:

import UIKit

class ExpenseItemTableViewCell: UITableViewCell 

    @IBOutlet weak var whenLabel: UILabel!

    @IBOutlet weak var whatLabel: UILabel!

    @IBOutlet weak var locationLabel: UILabel!

    @IBOutlet weak var priceLabel: UILabel!

    override func awakeFromNib() 
        super.awakeFromNib()
        // Initialization code
    

    override func setSelected(selected: Bool, animated: Bool) 
        super.setSelected(selected, animated: animated)
        // Configure the view for the selected state
    

我使用托管对象:

import Foundation
import CoreData

@objc(ExpenseItem)
class ExpenseItem: NSManagedObject 

    @NSManaged var when: NSDate
    @NSManaged var location: String
    @NSManaged var what: String
    @NSManaged var howMuch: NSDecimalNumber


当从 DetailViewController 的 prepareForSegue() 展开 segue either 时,我调用了一个函数来创建一个 NSManagedObject:

import UIKit

protocol ExpenseItemManagementDelegate 
    func insertExpenseItem(something: String, somePrice: String, somewhere: String, sometime: NSDate)


class DetailViewController: UIViewController 

    var delegate: ExpenseItemManagementDelegate!

    @IBOutlet weak var whenInput: UIDatePicker!

    @IBOutlet weak var whatInput: UITextField!

    @IBOutlet weak var locationInput: UITextField!

    @IBOutlet weak var priceInput: UITextField!

    var expenseItem: ExpenseItem? 
        didSet 
            // Update the view.
            self.configureView()
        
    

    func configureView() 
        // Update the user interface for the detail item.
        if let item: AnyObject = self.expenseItem 
            if let input = self.whenInput 
                input.date = item.valueForKey("when")! as NSDate
            
            if let input = self.whatInput 
                input.text = item.valueForKey("what")! as String
            
            if let input = self.locationInput 
                input.text = item.valueForKey("location")! as String
            
            if let input = self.priceInput 
                input.text = item.valueForKey("howMuch")! as String
            
        
    

    override func viewDidLoad() 
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.configureView()
    

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

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) 
        delegate.insertExpenseItem(self.whatInput!.text,somePrice:self.priceInput!.text,somewhere:self.locationInput!.text,sometime:self.whenInput!.date)
    

来自 MasterViewController 的 unwindToSegue():

@IBAction func unwindToSegue (segue : UIStoryboardSegue) 
    if let dvc: DetailViewController = segue.sourceViewController as? DetailViewController
    
        self.insertExpenseItem(dvc.whatInput!.text,somePrice:dvc.priceInput!.text,somewhere:dvc.locationInput!.text,sometime:dvc.whenInput!.date)
    

(显然,我使用不要同时从两个地方调用该函数)

通过单击 DetailViewController 导航栏中的 Cancel 或 Save 来展开 segue 调用 insertExpenseItem() 函数,并且来自输入的数据不用于创建 NSManagedObject。

func insertExpenseItem(something: String, somePrice: String, somewhere: String, sometime: NSDate) 
    let context = self.fetchedResultsController.managedObjectContext
    let entity = self.fetchedResultsController.fetchRequest.entity!
    let expenseManagedObject = NSEntityDescription.insertNewObjectForEntityForName("ExpenseItem", inManagedObjectContext: context) as ExpenseItem
    expenseManagedObject.what = "big mac" // you can now use dot syntax instead of setValue
    expenseManagedObject.howMuch = 500
    expenseManagedObject.location = "macdo"
    expenseManagedObject.when = NSDate()

    // Save the context.
    var error: NSError? = nil
    if !context.save(&error) 
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        //println("Unresolved error \(error), \(error.userInfo)")
        abort()
    

我尝试在 configureCell() 中设置自定义单元格标签中的值:

func configureCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath) 
    let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as ExpenseItem
    let expenseItem = cell as ExpenseItemTableViewCell
    expenseItem.whenLabel.text = object.when.description
    expenseItem.whatLabel.text = object.what
    expenseItem.locationLabel.text = object.location
    expenseItem.priceLabel.text = object.howMuch.description
    expenseItem.backgroundColor = UIColor.orangeColor()

我遇到的问题是在controllerDidChangeContent():

func controllerDidChangeContent(controller: NSFetchedResultsController) 
    self.tableView.endUpdates()
    // * or *
    //self.tableView.reloadData()

如果我调用 self.tableView.reloadData(),configureCell() 不会被调用,并且表格视图会在单元格应位于的位置显示一个完全空白的空间,但应用程序不会崩溃 如果我调用 self.tableView.endUpdates(),configureCell() 会被调用,但我会收到 EXC_BAD_ACCESS(code=2, address=...) 错误

2014/12/08 更新 这是堆栈跟踪:

Thread 1
Queue : com.apple.main-thread (serial)
#0  0x000000010a3c6324 in CA::Layer::ensure_transaction_recursively(CA::Transaction*) ()
[...]
#50 0x000000010a3c639a in CA::Layer::ensure_transaction_recursively(CA::Transaction*) ()
#130390 0x000000010a3c639a in CA::Layer::ensure_transaction_recursively(CA::Transaction*) ()
[...]
#130540 0x000000010a3c639a in CA::Layer::ensure_transaction_recursively(CA::Transaction*) ()
#130541 0x000000010a3cdd63 in CA::Layer::insert_sublayer(CA::Transaction*, CALayer*, unsigned long) ()
#130542 0x000000010a3ce14a in -[CALayer addSublayer:] ()
#130543 0x00000001064d2e30 in -[UIView(Internal) _addSubview:positioned:relativeTo:] ()
#130544 0x00000001067d9e01 in -[UITableViewCellLayoutManager layoutSubviewsOfCell:] ()
#130545 0x00000001066ec1d0 in -[UITableViewCell layoutSubviews] ()
#130546 0x00000001064d5973 in -[UIView(CALayerDelegate) layoutSublayersOfLayer:] ()
#130547 0x000000010a3d3de8 in -[CALayer layoutSublayers] ()
#130548 0x000000010a3c8a0e in CA::Layer::layout_if_needed(CA::Transaction*) ()
#130549 0x00000001064c9847 in -[UIView(Hierarchy) layoutBelowIfNeeded] ()
#130550 0x00000001064cd5ce in +[UIView(Animation) performWithoutAnimation:] ()
#130551 0x00000001065378de in __46-[UITableView _updateWithItems:updateSupport:]_block_invoke919 ()
#130552 0x00000001064ce362 in +[UIView(UIViewAnimationWithBlocks) _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:] ()
#130553 0x00000001064ce5b7 in +[UIView(UIViewAnimationWithBlocks) animateWithDuration:delay:options:animations:completion:] ()
#130554 0x0000000106536c71 in -[UITableView _updateWithItems:updateSupport:] ()
#130555 0x0000000106530ce7 in -[UITableView _endCellAnimationsWithContext:] ()
#130556 0x0000000105621c3b in myExpenseBook.MasterViewController.controllerDidChangeContent (myExpenseBook.MasterViewController)(ObjectiveC.NSFetchedResultsController) -> () at /Users/Ailete619/Desktop/myExpenseBook/myExpenseBook/MasterViewController.swift:241
#130557 0x0000000105621e2a in @objc myExpenseBook.MasterViewController.controllerDidChangeContent (myExpenseBook.MasterViewController)(ObjectiveC.NSFetchedResultsController) -> () ()
#130558 0x0000000105816241 in __77-[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:]_block_invoke ()
#130559 0x00000001058150b2 in -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] ()
#130560 0x0000000105ba0cec in __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ ()
#130561 0x0000000105aa08a4 in _CFXNotificationPost ()
#130562 0x0000000105fa96b8 in -[NSNotificationCenter postNotificationName:object:userInfo:] ()
#130563 0x000000010572cc96 in -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] ()
#130564 0x00000001057b5d1e in -[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:] ()
#130565 0x00000001057283d1 in -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] ()
#130566 0x000000010572b8f3 in -[NSManagedObjectContext save:] ()
#130567 0x000000010561709d in myExpenseBook.MasterViewController.insertExpenseItem (myExpenseBook.MasterViewController)(Swift.String, somePrice : Swift.String, somewhere : Swift.String, sometime : ObjectiveC.NSDate) -> () at /Users/Ailete619/Desktop/myExpenseBook/myExpenseBook/MasterViewController.swift:48
#130568 0x00000001056193af in myExpenseBook.MasterViewController.unwindToSegue (myExpenseBook.MasterViewController)(ObjectiveC.UIStoryboardSegue) -> () at /Users/Ailete619/Desktop/myExpenseBook/myExpenseBook/MasterViewController.swift:95
#130569 0x000000010561948a in @objc myExpenseBook.MasterViewController.unwindToSegue (myExpenseBook.MasterViewController)(ObjectiveC.UIStoryboardSegue) -> () ()
#130570 0x0000000106b28c2f in -[UIStoryboardUnwindSegueTemplate _perform:] ()
#130571 0x000000010645d8be in -[UIApplication sendAction:to:from:forEvent:] ()
#130572 0x000000010645d8be in -[UIApplication sendAction:to:from:forEvent:] ()
#130573 0x0000000106564410 in -[UIControl _sendActionsForEvents:withEvent:] ()
#130574 0x00000001065637df in -[UIControl touchesEnded:withEvent:] ()
#130575 0x00000001064a3308 in -[UIWindow _sendTouchesForEvent:] ()
#130576 0x00000001064a3c33 in -[UIWindow sendEvent:] ()
#130577 0x00000001064709b1 in -[UIApplication sendEvent:] ()
#130578 0x000000010647da7d in _UIApplicationHandleEventFromQueueEvent ()
#130579 0x0000000106459103 in _UIApplicationHandleEventQueue ()
#130580 0x0000000105b06551 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#130581 0x0000000105afc41d in __CFRunLoopDoSources0 ()
#130582 0x0000000105afba54 in __CFRunLoopRun ()
#130583 0x0000000105afb486 in CFRunLoopRunSpecific ()
#130584 0x0000000109cc79f0 in GSEventRunModal ()
#130585 0x000000010645c420 in UIApplicationMain ()
#130586 0x000000010561280e in top_level_code at /Users/Ailete619/Desktop/myExpenseBook/myExpenseBook/AppDelegate.swift:13
#130587 0x000000010561284a in main ()
#130588 0x0000000107eee145 in start ()
#130589 0x0000000107eee145 in start ()

我检查了我的托管对象是通过修改 controllerDidChangeContent() 创建的:

func controllerDidChangeContent(controller: NSFetchedResultsController) 
    let context = self.fetchedResultsController.managedObjectContext
    let objects = context.registeredObjects
    println("l=\(objects.count)")
    for o in objects 
        let e = o as ExpenseItem
        println("o when=\(e.when) what=\(e.what) location=\(e.location) price=\(e.howMuch)")
    
    self.tableView.endUpdates()

得到这个输出:

l=1
o when=2014-12-08 13:20:49 +0000 what=big mac location=macdo price=500

我的测试项目可在以下位置下载:ailete619com.appspot.com/***/myExpenseBook.zip

【问题讨论】:

仍在寻找有关我做错了什么以及如何找到崩溃原因的想法... 【参考方案1】:

通过在 Xcode 中设置异常断点来开始调试。然后你会确切地知道什么是崩溃。很可能不是表格视图本身,而是涉及您的核心数据堆栈或其他数据源的东西。

【讨论】:

我设置了一个异常断点,但是除了我所有的 println 消息之外我没有收到任何消息...我没有收到异常消息【参考方案2】:

只需在 prepareForSegue 消息中从 DestinationViewController 调用 insertExpenseItem。您可以将其更改为期望 ExpenseItem 而不是所有参数。

【讨论】:

你的意思是在 DetailViewController 中创建 ExpenseItem 可以解决崩溃问题?因为我尝试在 prepareForSegue() 中从 DestinationViewController 调用 insertExpenseItem(),正如您在上面添加的代码中看到的那样。

以上是关于IOS Swift Master Detail Application, managed object, tableView: endUpdates() 调用configureCell() 但崩溃,r的主要内容,如果未能解决你的问题,请参考以下文章

Swift 中的 Master Detail 应用程序 - 如何将字符串转换为 URL

UITableView 在使用 Swift 中的 Master Detail App 滚动之前不会重新加载

iOS 上的 Xamarin.Forms Master/Detail 边缘滑动

scrollsToTop 不适用于简单的 Master-Detail iOS 应用程序

iOS核心数据连接2x主明细表

UWP 快速的Master/Detail实现