核心数据:访问 NSManagedObject 属性时应用程序崩溃

Posted

技术标签:

【中文标题】核心数据:访问 NSManagedObject 属性时应用程序崩溃【英文标题】:Core Data: app crashed when accesing to NSManagedObject attribute 【发布时间】:2016-04-15 18:15:44 【问题描述】:

我描述了这个问题。我有一个 CoreData 实体“新”。我正在使用带有 tableView 的 fetchedResultsController 来呈现对象。问题是当应用尝试更新标签时,它崩溃了(单元格有两个标签,但应用总是在同一个句子中崩溃,更新相同的标签......)。

这里有一些代码:

这就是CoreDataManager的定义:

// MARK: - Shared Instance

/**
*  This class variable provides an easy way to get access
*  to a shared instance of the CoreDataStackManager class.
*/
class func sharedInstance() -> CoreDataStackManager 

    struct Singleton 
        static let instance = CoreDataStackManager()
    
    return Singleton.instance


// MARK: - The Core Data stack. The code has been moved, unaltered, from the AppDelegate.
lazy var applicationDocumentsDirectory : NSURL = 

    let urls  = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
    return urls[urls.count-1]
()

lazy var managedObjectModel: NSManagedObjectModel = 

    let modelURL = NSBundle.mainBundle().URLForResource("Model", withExtension: "momd")!
    return NSManagedObjectModel(contentsOfURL: modelURL)!
()

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = 

    let coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
    let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent(SQLITE_FILE_NAME)

    var failureReason = "There was an error creating or loading the application's saved data."
    do 
        try coordinator?.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
     catch 
        // Report any error we got.
        var dict = [String: AnyObject]()
        dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
        dict[NSLocalizedFailureReasonErrorKey] = failureReason

        dict[NSUnderlyingErrorKey] = error as NSError
        let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
        // Replace this 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.
        NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
        abort()
    

    return coordinator
()

lazy var managedObjectContext: NSManagedObjectContext = 

    let coordinator = self.persistentStoreCoordinator
    var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
    managedObjectContext.persistentStoreCoordinator = coordinator

    return managedObjectContext
()

// MARK: - Core Data Saving support
func saveContext() 

    if managedObjectContext.hasChanges 

        managedObjectContext.performBlockAndWait 
            do 
                try self.managedObjectContext.save()
             catch 
                let nserror = error as NSError
                NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
                abort()
            
        
    

这里,应用程序崩溃的地方:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 

    let cell = tableView.dequeueReusableCellWithIdentifier("HotNewsCell") as! NewsTableViewCell
    let new = fetchedResultsController.objectAtIndexPath(indexPath) as! New

    configureCell(cell, new: new)

    return cell


private func configureCell(cell: NewsTableViewCell, new: New) 

cell.titleLabel.text = new.title
/* Next line is where app crashes :( */
cell.descriptionLabel.text = new.newBody

if let url = NSURL(string: new.photoReference) 

    cell.activityIndicator.startAnimating()

    if new.image != nil 
        cell.imageViewNews.image = new.image
        cell.activityIndicator.stopAnimating()
    
    else 

        let task = NetworkRequests.sharedInstance().makeImageRequestFromURL(url)  (data, error) in

            guard error == nil else 
                print(error)
                return
            

            guard let data = data else 
                print("No data available")
                return
            

            let image = UIImage(data: data)
            new.image = image

            dispatch_async(dispatch_get_main_queue())
            
                cell.imageViewNews.image = image
                cell.activityIndicator.stopAnimating()
            
        

        cell.taskToCancelifCellIsReused = task
    

这很奇怪,因为如果我评论“cell.descriptionLabel.text = new.newBody”,应用程序就会完美运行。

谢谢!

编辑

错误是 EXC_BAD_ACCESS。然后,我启用了 NSZombie,错误是“message sent to deallocated instance”。

【问题讨论】:

应用程序崩溃怎么办?有任何错误信息吗? exc_bad_access。启用 NSZombie 后,报错:message sent to deallocated instance 再次仔细检查descriptionLabel 在您的故事板或任何地方是真实的。它是否命名并正确连接到单元格? 是的,它已正确连接。我在其他 ViewController 中使用这个 Custom Cell 而不使用 CoreData 并且工作正常......我认为这是 CoreData 的问题,但我不知道究竟是什么问题...... 尝试更改 newBody 属性名称 - 我隐约记得以 new.... 开头的属性存在问题。 【参考方案1】:

我相信这是使用以new.... 开头的属性名称的产物。为避免此问题,只需更改属性名称即可。

另请参阅this answer 的一个无关问题,该问题突出了new... 的问题。从该答案(Clang 文档)中确定的document 来看,如果紧跟在“new”之后的字符是小写字母,则从技术上讲,似乎不会出现问题。所以newbody 可以,而newBody 不行。此外,为了完整起见,同样应避免使用以下前缀(尽管它们不太可能成为属性名称的选择):

分配 复制 可变复制 初始化

有趣的是,Xcode(至少是 7.3 版;不确定以前的版本)在 Objective-C 中不允许使用这些属性名称,从而导致编译器错误:

属性遵循 Cocoa 命名约定以返回“拥有”对象

遗憾的是 Swift 编译器没有给出任何错误或警告。

【讨论】:

以上是关于核心数据:访问 NSManagedObject 属性时应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

Swift 中的 NSManagedObject 子类不能使用自定义访问器?

从众多实体中恢复一个获取的实体——核心数据

Swift Core Data - 使用 NSManagedObject 子类访问获取的实体时出错

NSManagedObject 保存在核心数据中但属性错误

核心数据原始访问器

是不是不能复制一个核心数据NSManagedObject?