Сбой ABRecordCopyValue и ABPropertyID - PullRequest
2 голосов
/ 13 октября 2011

Я пытаюсь получить данные из адресной книги iPhone, и у меня возникают некоторые проблемы.

Прежде всего, у меня есть массив всех контактов (self.allContacts):


ABAddressBookRef abRef = ABAddressBookCreate();     
self.allContacts = (NSMutableArray*)ABAddressBookCopyArrayOfAllPeople(abRef);

У меня также есть массив всех свойств (каждое свойство в виде строки) с именем self.allKeys.

Сбой происходит, когда я пытаюсь получить свойства, используя свойство из self.allKeys:


NSString *currentRecord = [[NSString alloc] init];
ABRecordRef currentRecordRef;
ABPropertyID currentFieldProperty;

currentRecordRef =  (ABRecordRef)[self.allContacts objectAtIndex:i];
currentFieldProperty = (ABPropertyID)[self.allKeys objectAtIndex:j];

currentRecord = (NSString*)ABRecordCopyValue(currentRecordRef, currentFieldProperty);                                   

Проблема в том, что передача currentFieldProperty в ABRecordCopyValue вызывает сбой.


  • self.allContacts - это массив всех контактов
  • self.allKeys - это массив всех свойств (каждое свойство в виде строки)

При попытке извлечь одно свойство из ABRecordCopyValue это вызывает сбой EXC_BAD_ACCESS.

Спасибо!

Ответы [ 3 ]

4 голосов
/ 15 октября 2011

Установить NSZombieEnabled , MallocStackLogging и guard malloc в отладчике.Затем, когда произойдет сбой вашего приложения, введите его в консоли gdb:

(gdb) info malloc-history 0x543216

Замените 0x543216 на адрес объекта, вызвавшего сбой, и вы получите гораздо более полезную трассировку стека, и это должно помочьВы указываете точную строку в своем коде, которая вызывает проблему.


- Дополнительные мысли -

Если self.allKeys действительно

"массив всех свойств (каждое свойство в виде строки) "

, тогда вы, вероятно, должны получить intValue объекта массива (свойства), поскольку ABPropertyID - это просто typedef int32_t.Что-то вроде:

currentFieldProperty = (ABPropertyID)[[self.allKeys objectAtIndex:j] intValue];
ABRecordCopyValue(currentRecordRef, currentFieldProperty)

Но нам нужно увидеть значения в self.allKeys или как оно заполнено, чтобы быть уверенным.

Из Ссылка ABRecordRef и CFTypeRef Reference

ABRecordCopyValue - Возвращает значение свойства записи.

    CFTypeRef ABRecordCopyValue (
       ABRecordRef record,
       ABPropertyID property
    );

Параметры record - Запись, содержащая данное свойство. property - Свойство записи, значение которой возвращается.

Возвращаемое значение Значение свойства в записи.

И:

ABPropertyID - Целое число, идентифицирующее свойство записи. typedef int32_t ABPropertyID;


- и другие идеи по устранению неполадок -

Если вышеприведенное не так, то ваш сбой может быть вызван тем, что вы произвели CFTypeRef на NSString * в (NSString*)ABRecordCopyValue(currentRecordRef, currentFieldProperty), так вот небольшая вспомогательная функция, которая может решить эту проблему:

- (NSString*)stringValueForCFType:(CFTypeRef)cfValue {
    NSString *stringValue = nil;
    if (!cfValue) return nil;
    CFTypeID cfType = CFGetTypeID(cfValue);
    if (cfType == CFStringGetTypeID()) {
        stringValue = [[(id)CFMakeCollectable(cfValue) retain] autorelease];
    } else if (cfType == CFURLGetTypeID()) {
        stringValue = [(NSURL*)cfValue absoluteString];
    } else if (cfType == CFNumberGetTypeID()) {
        stringValue = [(NSNumber*)cfValue stringValue];
    } else if (cfType == CFNullGetTypeID()) {
        stringValue = [NSString string];
    } else if (cfType == AXUIElementGetTypeID()) {
        stringValue = [[GTMAXUIElement elementWithElement:cfValue] description];
    } else if (cfType == AXValueGetTypeID()) {
        stringValue = [self stringValueForAXValue:cfValue];
    } else if (cfType == CFArrayGetTypeID()) {
        stringValue = [self stringValueForCFArray:cfValue];
    } else if (cfType == CFBooleanGetTypeID()) {
        stringValue = CFBooleanGetValue(cfValue) ? @"YES" : @"NO";
    } else {
        CFStringRef description = CFCopyDescription(cfValue);
        stringValue = [(id)CFMakeCollectable(description) autorelease];
  }
  return stringValue;       
}

Затем выполните currentRecord = [self stringValueForCFType:ABRecordCopyValue(currentRecordRef, currentFieldProperty)]; и убедитесь, что self.allKeys имеет объект с индексами j и self.allContacts имеет объект с индексом i:

NSString *currentRecord = [[NSString alloc] init];
ABRecordRef currentRecordRef;
ABPropertyID currentFieldProperty;

if (self.allContacts.count > i) {
    currentRecordRef = (ABRecordRef)[self.allContacts objectAtIndex:i];    
    if (self.allKeys.count > j) {
        currentFieldProperty = (ABPropertyID)[self.allKeys objectAtIndex:j];
        currentRecord = [self stringValueForCFType:ABRecordCopyValue(currentRecordRef, currentFieldProperty)];
    } else {
        NSLog(@"self.allKeys has no value at index (%d): %@", j, [allKeys description]);
    }
} else {
    NSLog(@"self.allContacts has no value at index (%d): %@", i, [allContacts description]);
}

Редактировать (относительно комментариев):

Чтобы преобразовать строку имени свойства в его значение int, вам необходимо создатьследующее (вероятно, это не тот порядок, в котором они должны быть, поэтому NSLog сначала посмотрите, в каком порядке они должны быть):

NSString * const ABPropertyID_toString[] = {
    @"kABPersonFirstNameProperty",
    @"kABPersonLastNameProperty",
    @"kABPersonMiddleNameProperty",
    @"kABPersonPrefixProperty",
    @"kABPersonSuffixProperty",        
    @"kABPersonNicknameProperty", 
    @"kABPersonFirstNamePhoneticProperty",
    @"kABPersonLastNamePhoneticProperty", 
    @"kABPersonMiddleNamePhoneticProperty", 
    @"kABPersonOrganizationProperty", 
    @"kABPersonJobTitleProperty", 
    @"kABPersonDepartmentProperty", 
    @"kABPersonEmailProperty", 
    @"kABPersonBirthdayProperty", 
    @"kABPersonNoteProperty", 
    @"kABPersonCreationDateProperty", 
    @"kABPersonModificationDateProperty", 
    @"kABPersonAddressProperty", 
    // ... etc
};

- (NSString *)ABPropertyIDToString:(ABPropertyID)propertyVal {
    return ABPropertyID_toString[propertyVal];
}

- (ABPropertyID)stringToABPropertyID:(NSString *)propertyString {
    int retVal;
    for(int i=0; i < sizeof(ABPropertyID_toString) - 1; ++i) {
        if([(NSString *)ABPropertyID_toString[i] isEqual:propertyString]) {
            retVal = i;
            break;
        }
    }
    return retVal;
}

Затем передайте stringToABPropertyID:значение из массива [self.allKeys objectAtIndex:j] и вам будет возвращено ABPropertyID:

currentFieldProperty = [self stringToABPropertyID:[self.allKeys objectAtIndex:j]];
0 голосов
/ 30 мая 2012

Несколько проблем:

  • ABPropertyID не является типом объекта. (ABPropertyID)[self.allKeys objectAtIndex:j] явно не так. Я не уверен, как вообще возможно быть «массивом всех свойств (каждое свойство в виде строки)». О какой строке ты говоришь? Откуда вы взяли эту строку? Если вы говорите о строке названия объекта, например, @"kABPersonEmailProperty" тогда было бы почти невозможно получить из этого значение.
  • Значения ABPropertyID, такие как kABPersonEmailProperty, не являются константами. Они являются переменными, и, похоже, они инициализируются только после первой инициализации адресной книги.
0 голосов
/ 15 октября 2011

Я публикую свой предыдущий комментарий в качестве ответа:

Я бы особо подумал о значении currentFieldProperty - поскольку он извлекается из словаря, это объект, но функция должна получить перечисляемое значение. Может быть, попробуйте

currentFieldProperty = (ABPropertyID)[[self.allKeys objectAtIndex:j] intValue];
...