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;
}