Pregunta

Así que estoy tratando de convertir un proyecto antiguo en conteo automático de referencias. Estoy tratando de usar la herramienta de conversión que tiene Xcode, pero dice que arreglar un par de cosas antes de que pueda convertirse. No tengo idea de cómo corregir este error. Está en la implementación del archivo del llavero. Este método es el que devuelve el error, específicamente la línea con el secitemCopyMatching. El error que recibo dice: "Elenco de un puntero indirecto a un puntero de objetivo-C a 'cftypeRef*' (también conocido como 'const void **') está rechazado con arco. He estado buscando en todo Google, Apple Docs, y un montón de otra basura y no puede encontrar una mejor manera de obtener un diccionario de datos existente en el llavero. Cualquier ayuda apreciada. ¡Gracias!

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

}

¿Fue útil?

Solución

No necesitas deshabilitar ARC para esto; Solo necesita declarar el resultado de la consulta como un CFDictionaryRef, luego létalo a un NSDictionary Después de la llamada.

/*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;

Un par de comentarios:

  • En la línea 1, convertimos la consulta de Cocoa Land a Core Foundation Country. Usamos __bridge_retained Para asegurarse de que ARC no se libere y repare el objeto mientras trabajamos con él. Este tipo de yeso de puente conserva el objeto, por lo que para evitar fugas, debe seguirse con un correspondiente CFRelease en algún lugar. SecItemCopyMatching Definitivamente no lanzaremos la consulta para nosotros, por lo que si usamos un puente retenido, entonces necesitamos lanzar el objeto de base central resultante nosotros mismos. (Que hacemos en la línea 4.)
  • Las líneas 2, 3 y 4 son código C puro que usan tipos de fundamento central, por lo que ARC no tendrá nada que hacer o quejarse de ellas.
  • En la línea 5, le decimos a Arc que SecItemCopyMatching ha creado su resultado con un recuento de retención de 1, que somos responsables de liberar. (Sabemos esto porque tiene "copia" en su nombre). __bridge_transfer Hemos sabemos sobre esta responsabilidad, por lo que podrá hacerlo automáticamente por nosotros.
  • No lanzar el Diccionario de la Fundación Core Immutable devuelto por SecitemCopyMatching a NSMutableDictionary; eso está mal. Además, es contra las convenciones generales de estilo de cacao que buildSearchQuery Devuelve un NSMutableDictionary. Simple NSDictionaryS funcionaría bien en ambos casos.

La regla general aquí es que __bridge_retained necesita ser seguido por un CFRelease, mientras que el resultado de una función de "copia" o "crear" debe ser arrojada a coco-tierras usando __bridge_transfer.

Otros consejos

Método 3: Deje que ARC haga el trabajo pesado (o una combinación de Método 1 y Método 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 no necesita ser dueño del diccionario entrante, por lo que __bridge es adecuado y luego ARC continúa administrando la vida útil de la consulta.

Y al transferir la propiedad del resultado al ARC, también administrará la vida útil de los resultados y nos liberará de la necesidad de recordar que se acelere en todas las rutas de código.

Método 2: Cuando lo usa una vez, ¿por qué necesita una retención o transferencia? Ejemplo del trabajo inferior en Glance for Me, pruebas, depuración (memleaks) Todos pasan :) Solo una cosa filtró variable autoretenida inédita cuando se encontrará clave (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;
 }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top