当UITableView重复加载Core Data时,应用程序崩溃,糟糕的指针?

时间:2015-01-27 01:02:24

标签: ios uitableview swift abaddressbook

我正在创建一个应用,用户从地址簿中选择一个联系人并将其添加到表格视图中。

我通过创建一个addressBook对象,使用peoplePicker实现了这一点,将Core Data中的ABRecordID对象存储为NSNumber,从Core Data中提取ABRecordID并将其转换为ABRecordRef,然后获取kABPersonFirstNameProperty。

首次加载应用时,它运行正常。选择名称并将其添加到Core Data数据库,但在重复选择2-5名称后,应用程序崩溃为“EXC_BAD_ACCESS(code = 1,address = 0x3 ...)。这发生在cellForRowAtIndexPath tableView调用底部代码块。

我无法弄清楚为什么它会在工作几次后崩溃。有时它会工作两次,其他的则会工作五次。

非常感谢任何帮助。谢谢!!

这是我的代码:

import UIKit
import CoreData
import AddressBook
import AddressBookUI

class ViewController: UIViewController, UITableViewDataSource, ABPeoplePickerNavigationControllerDelegate {

@IBOutlet weak var tableView: UITableView!

var contacts = [NSManagedObject]()

@IBAction func addContact(sender: AnyObject) {

    // nav controller
    let picker = ABPeoplePickerNavigationController()

    // delegate so we can get the selected contact and info
    picker.peoplePickerDelegate = self

    // make only some people selectable, others grayed out
    //picker.predicateForEnablingPerson = NSPredicate(format: "%K like %@", ABPersonFamilyNameProperty, "Neuburg")

    // make sure we can see contact info
    picker.predicateForSelectionOfPerson = NSPredicate(value:true)

    // all done
    self.presentViewController(picker, animated:true, completion:nil)

}

// called when user picks a contact to get name
func peoplePickerNavigationController(peoplePicker: ABPeoplePickerNavigationController!, didSelectPerson contact: ABRecord!) {

    // ABRecordRefID object to use and store
    let contactABRecordID: Int32 = ABRecordGetRecordID(contact)

    // save the contact into Core Data
    self.saveContact(contactABRecordID)

    // reload data to table
    self.tableView.reloadData()
}

func saveContact(contactABRecordID: Int32) {

    // get access to managedObjectContext through the app delegate
    let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate

    let managedContext = appDelegate.managedObjectContext!

    // create new managed object and instert into the managed object context
    let entity = NSEntityDescription.entityForName("Contact", inManagedObjectContext: managedContext)

    let contact = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: managedContext)

    let wrapper = NSNumber(int: contactABRecordID)

    // set abrecordid attribute for NSMangedObject using KVC
    contact.setValue(wrapper, forKey: "abRecordID")

    // commit changes to person and save to disk
    var error: NSError?
    if !managedContext.save(&error) {
        println("Could not save \(error), \(error?.userInfo)")
    }

    // append the new person to contacts database
    contacts.append(contact)

}

// fetch data from Core Data
override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    // pull up app delegate and grab a ref to managed object context
    let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate

    let managedContext = appDelegate.managedObjectContext!

    // fetch data from the database
    let fetchRequest = NSFetchRequest(entityName: "Contact")

    // create error var
    var error: NSError?

    // return an array of managed objects that meets critera from fetch request
    let fetchedResults = managedContext.executeFetchRequest(fetchRequest, error: &error) as [NSManagedObject]?

    // make sure fetch worked
    if let results = fetchedResults {
        contacts = results
    } else {
        println("Could not fetch \(error), \(error!.userInfo)")
    }

}

// MARK: - people picker protocol

// address book object
var addressBook : ABAddressBook!

// check to see if app has access to contacts
func determineStatus() -> Bool {

    // assign status of access
    let status = ABAddressBookGetAuthorizationStatus()

    // handle status
    switch status {
    case .Authorized:
        // create address book if authoriezed
        return self.createAddressBook()
    case .NotDetermined:
        // ask for permission if haven't answered
        var ok = false
        ABAddressBookRequestAccessWithCompletion(nil) {
            (granted:Bool, err:CFError!) in
            dispatch_async(dispatch_get_main_queue()) {
                if granted {
                    ok = self.createAddressBook()
                }
            }
        }
        // proceed if access is granted
        if ok == true {
            return true
        }
        // otherwise return false
        self.addressBook = nil
        return false
        // if access is restricted
    case .Restricted:
        self.addressBook = nil
        return false
    case .Denied:
        // new iOS 8 feature: sane way of getting the user directly to the relevant prefs
        // I think the crash-in-background issue is now gone
        let alert = UIAlertController(title: "Need Authorization", message: "Wouldn't you like to authorize this app to use your Contacts?", preferredStyle: .Alert)
        alert.addAction(UIAlertAction(title: "No", style: .Cancel, handler: nil))
        alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: {
            _ in
            let url = NSURL(string:UIApplicationOpenSettingsURLString)!
            UIApplication.sharedApplication().openURL(url)
        }))
        self.presentViewController(alert, animated:true, completion:nil)
        self.addressBook = nil
        return false
    }
}

// create address book to use if granted access
func createAddressBook() -> Bool {

    // return true if created successfully
    if self.addressBook != nil {
        return true
    }

    // handle errors
    var err : Unmanaged<CFError>? = nil
    let addressBook : ABAddressBook? = ABAddressBookCreateWithOptions(nil, &err).takeRetainedValue()
    if addressBook == nil {
        println(err)
        self.addressBook = nil
        return false
    }

    // assign addresss book with error then return
    self.addressBook = addressBook
    return true
}

// when app opens. make sure we have permission for contacts
override func viewDidAppear(animated: Bool) {

    super.viewDidAppear(animated)

}

// =========

// MARK: - table view protocol

// table will have as many rows as contacts in array
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    // number of contacts in contacts array
    return contacts.count
}

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

    // each individual cell
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell

    // contact is one of contacts in database
    let contact = contacts[indexPath.row]

    // get ABRecordID
    let wrapper: NSNumber = contact.valueForKey("abRecordID")! as NSNumber

    // turn wrapper into Int32 type
    let contactABRecordID: Int32 = Int32(wrapper.integerValue)

    // ERROR COMES HERE!!!! get person with record ID
    let personABRecordRef: ABRecordRef = ABAddressBookGetPersonWithRecordID(addressBook, contactABRecordID).takeRetainedValue() as ABRecordRef

    // get name from person with record ref
    let firstName: String = ABRecordCopyValue(personABRecordRef, kABPersonFirstNameProperty).takeRetainedValue() as String

    // grab name attribute from managed object
    cell.textLabel!.text = firstName

    // give cell to table
    return cell

}

// =========

// MARK: - standard code

override func viewDidLoad() {
    super.viewDidLoad()

    // determine if we have access to contacts
    self.determineStatus()

    // bring up a box in front if we don't
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "determineStatus", name: UIApplicationWillEnterForegroundNotification, object: nil)

    // set the title
    title = "My People"

    // register UITableViewCell with tableView
    tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}

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

// =========

}

解决!

这就是我所做的。我需要使用一个无人的abperson ref并解开它。

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

    // each individual cell
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell

    // contact is one of contacts in database
    let contact = contacts[indexPath.row]

    // grab name attribute from managed object
    cell.textLabel!.text = getContactInfoFromCoreData(contact: contact)

    // give cell to table
    return cell

}

// =========

// MARK: - get contact from Core Data

func getContactInfoFromCoreData(#contact: NSManagedObject) -> String {

    // get ABRecordID
    let wrapper: NSInteger = contact.valueForKey("abRecordID")! as NSInteger

    // turn wrapper into Int32 type
    let contactABRecordID: ABRecordID = Int32(wrapper)

    // get person ref with record ID
    let unmanagedPersonABRecordRef: Unmanaged<ABRecordRef>? = ABAddressBookGetPersonWithRecordID(addressBook, contactABRecordID)

    // unwrap optional ref to get real ref
    if let personRefToUse: ABRecordRef = unmanagedPersonABRecordRef?.takeUnretainedValue() {

        // use unwrapped ref to get property
        let firstName: String = ABRecordCopyValue(personRefToUse, kABPersonFirstNameProperty).takeRetainedValue() as String

        // return to table view
        return firstName

    }

    // not good
    return "Something went wrong"

}

// =========

1 个答案:

答案 0 :(得分:0)

这是用原始代码实现的解决方案。我需要使用一个可选项并打开它。

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

    // each individual cell
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell

    // contact is one of contacts in database
    let contact = contacts[indexPath.row]

    // grab name attribute from managed object
    cell.textLabel!.text = getContactInfoFromCoreData(contact: contact)

    // give cell to table
    return cell

}

// =========

// MARK: - get contact from Core Data

func getContactInfoFromCoreData(#contact: NSManagedObject) -> String {

    // get ABRecordID
    let wrapper: NSInteger = contact.valueForKey("abRecordID")! as NSInteger

    // turn wrapper into Int32 type
    let contactABRecordID: ABRecordID = Int32(wrapper)

    // get person ref with record ID
    let unmanagedPersonABRecordRef: Unmanaged<ABRecordRef>? = ABAddressBookGetPersonWithRecordID(addressBook, contactABRecordID)

    // unwrap optional ref to get real ref
    if let personRefToUse: ABRecordRef = unmanagedPersonABRecordRef?.takeUnretainedValue() {

        // use unwrapped ref to get property
        let firstName: String = ABRecordCopyValue(personRefToUse, kABPersonFirstNameProperty).takeRetainedValue() as String

        // return to table view
        return firstName

    }

    // not good
    return "Something went wrong"

}

// =========