iPhone получить словарь данных из брелка

Поэтому я пытаюсь преобразовать старый проект в автоматический подсчет ссылок. Я пытаюсь использовать инструмент преобразования, который есть в xCode, но он говорит, чтобы исправить несколько вещей, прежде чем он сможет конвертировать. Я понятия не имею, как исправить эту ошибку. Это в реализации файла цепочки для ключей. Этот метод возвращает ошибку, в частности строку с SecItemCopyMatching. Я получаю сообщение об ошибке: "Преобразование косвенного указателя на указатель Objective C на" CFTypeRef* "(он же" const void** ") запрещено с ARC. Я просматривал Google, Apple Docs, и кучу других дерьмов, и не могу найти лучший способ получить существующий словарь данных в цепочке для ключей. Любая помощь приветствуется. Спасибо!

-(NSMutableDictionary*)fetchDictionary {

NSMutableDictionary *genericPasswordQuery = [self buildSearchQuery];

NSMutableDictionary *outDictionary = nil;
OSStatus status = SecItemCopyMatching((__bridge_retained  CFDictionaryRef)genericPasswordQuery, (CFTypeRef*)&outDictionary);

if (DEBUG) printf("FETCH: %s\n", [[self fetchStatus:status] UTF8String]);

if (status == errSecItemNotFound) return NULL;
return outDictionary;

}

3 ответа

Решение

Вам не нужно отключать ARC для этого; вам просто нужно объявить результат запроса как CFDictionaryRefзатем брось NSDictionary после звонка.

/*1*/ CFDictionaryRef cfquery = (__bridge_retained CFDictionaryRef)genericPasswordQuery;
/*2*/ CFDictionaryRef cfresult = NULL;
/*3*/ OSStatus status = SecItemCopyMatching(cfquery, (CFTypeRef *)&cfresult);
/*4*/ CFRelease(cfquery);
/*5*/ NSDictionary *result = (__bridge_transfer NSDictionary *)cfresult;

Пара замечаний:

  • В строке 1 мы конвертируем запрос из земли какао в страну основного фонда. Мы используем __bridge_retained чтобы убедиться, что ARC не освобождает и не освобождает объект, пока мы с ним работаем. Этот тип связующего броска сохраняет объект, поэтому для предотвращения утечек, он должен сопровождаться соответствующим CFRelease где-то. SecItemCopyMatching определенно не выпустит запрос для нас, поэтому, если мы используем оставленный мост, то нам нужно освободить получившийся объект Core Foundation самостоятельно. (Что мы делаем в строке 4.)
  • Строки 2, 3 и 4 представляют собой чистый код C, использующий типы Core Foundation, поэтому ARC не будет ничего делать или жаловаться на них.
  • В строке 5 мы сообщаем ARC, что SecItemCopyMatching создал свой результат с счетом сохранения 1, который мы несем ответственность за выпуск. (Мы знаем это, потому что в его имени есть "Копия".) __bridge_transfer уведомить ARC об этой ответственности, чтобы она смогла сделать это за нас автоматически.
  • Не приводите неизменный словарь Core Foundation, возвращенный SecItemCopyMatching, к NSMutableDictionary; это просто неправильно. Кроме того, это против общих соглашений стиля Какао, что buildSearchQuery возвращает NSMutableDictionary, просто NSDictionaryс будет хорошо работать в обоих случаях.

Эмпирическое правило здесь таково, что __bridge_retained должен сопровождаться CFReleaseв то время как результат функции "Копировать" или "Создать" должен быть приведен в Cocoa-land с использованием __bridge_transfer,

Метод 3: Пусть ARC выполняет тяжелую работу (или комбинацию метода 1 и метода 2):

NSMutableDictionary* query = [NSMutableDictionary dictionaryWithDictionary:
@{
    (__bridge id) kSecClass : (__bridge id) kSecClassGenericPassword,
    (__bridge id) kSecAttrService : nssService,
#if ! TARGET_IPHONE_SIMULATOR
    (__bridge id) kSecAttrAccessGroup : @"PRODUCT.com.COMPANY.GenericKeychainSuite",
#endif

    (__bridge id) kSecMatchLimit : (__bridge id) kSecMatchLimitOne,
    (__bridge id) kSecReturnAttributes : (__bridge id) kCFBooleanTrue,
}];

if ( [nssAccount length] != 0 )
    [query setObject:nssAccount forKey:(__bridge id) kSecAttrAccount];

CFDictionaryRef cfresult;
auto err =  ::SecItemCopyMatching((__bridge CFDictionaryRef)query,
                                  (CFTypeRef*)&cfresult);
if ( err == errSecItemNotFound )
    return std::wstring();
else if ( err != noErr)
    throw std::exception();

NSDictionary* result = (__bridge_transfer NSDictionary*) cfresult;

SecItemCopyMatching не должен владеть входящим словарем, поэтому __bridge является адекватным, и тогда ARC продолжает управлять временем жизни запроса.

И, передав право собственности на результат в arc, он также будет управлять временем жизни результата и избавит нас от необходимости помнить CFRelease о всех путях кода.

Способ 2: Когда вы используете его один раз, зачем нуждаться в сохранении или передаче? пример из нижней части работы с первого взгляда для меня, тестирование, отладка (memleaks) все проходит:) Только одна вещь утечка неизданной автоматически сохраняемой переменной, когда ключ будет найден (1)

CFDictionaryRef keyAttributes = NULL;  /* variable for store attributes */ 

 NSMutableDictionary *credQuery = [NSMutableDictionary dictionary]; // credential Query 

 /* Here you add some options for search your key */

 OSStatus errGather = SecItemCopyMatching(
    (__bridge CFDictionaryRef)credQuery,
    (CFTypeRef *)&keyAttributes
 );

 if (errGather == errSecSuccess) {
    // Gather stored key
    NSDictionary *keychainDict = (__bridge NSDictionary *)keyAttributes;
    NSData *passData = keychainDict[(__bridge id<NSCopying>)kSecValueData]; // password
    ...
    /* work with gathered data :) */
    ...
    CFRelease(keyAttributes); // (1) HERE. Release when CFType is retained really :)
    credQuery = nil;
    keychainDict = nil;
    passData = nil;
 }
Другие вопросы по тегам