iPhone получить словарь данных из брелка - PullRequest
9 голосов
/ 20 октября 2011

Поэтому я пытаюсь преобразовать старый проект в автоматический подсчет ссылок. Я пытаюсь использовать инструмент преобразования, который есть в 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 ]

42 голосов
/ 04 ноября 2011

Вам не нужно отключать 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 не будет ничего делать или жаловаться на них.
  • InВ строке 5 мы сообщаем ARC, что SecItemCopyMatching создал свой результат с счетом сохранения 1, который мы несем ответственность за освобождение.(Мы знаем это, потому что в его имени есть «Копия».) __bridge_transfer позволяет ARC знать об этой ответственности, поэтому он сможет сделать это для нас автоматически.
  • Не разыгрывайте неизменное ЯдроОсновной словарь, возвращаемый SecItemCopyMatching в NSMutableDictionary;это просто неправильно.Кроме того, это против общих соглашений стиля Какао, что buildSearchQuery возвращает NSMutableDictionary.Простые NSDictionary s будут хорошо работать в обоих случаях.

Практическое правило заключается в том, что за __bridge_retained должно следовать CFRelease, в то время как результат из "Копии" илиФункция «Создать» должна быть приведена в Cocoa-land с помощью __bridge_transfer.

2 голосов
/ 07 февраля 2013

Метод 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 для всех путей кода.

0 голосов
/ 05 сентября 2012

Метод 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;
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...