2011-10-19 18 views
9

Por lo tanto, estoy tratando de convertir un proyecto antiguo en Cuenta de referencia automática. Estoy tratando de usar la herramienta de conversión que xCode tiene pero dice que debe arreglar un par de cosas antes de que pueda convertirse. No tengo idea de cómo solucionar este error. Está en la implementación del archivo de llavero. Este método es el que devuelve el error, específicamente la línea con SecItemCopyMatching. El error que recibo dice: "El lanzamiento de un puntero indirecto a un puntero de Objective-C a 'CFTypeRef *' (también conocido como 'const void **') no se permite con ARC. He estado buscando en google, apple docs, y un montón de otras porquería y no puede encontrar una mejor manera a buscar un diccionario de datos existentes en el llavero. Cualquier ayuda apreciada. Gracias!iPhone fetch data dictionary from keychain

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

}

Respuesta

41

no es necesario desactivar ARC para ello; sólo tiene que declarar el resultado de la consulta como un CFDictionaryRef, luego echarlo 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; 
.

par de comentarios:

  • En la línea 1, que convierte la consulta de cacao de la tierra a otro Fundación Core. Utilizamos __bridge_retained para asegurarnos de que ARC no libera y desasigna el objeto mientras trabajamos con él. Este tipo de conversión puente conserva el objeto, por lo que para evitar fugas, debe seguirse con un CFRelease correspondiente en algún lugar. SecItemCopyMatching definitivamente no lanzará la consulta por nosotros, por lo que si usamos un puente retenido, entonces tenemos que liberar el objeto Core Foundation resultante nosotros mismos. (Lo cual hacemos en la línea 4).
  • Las líneas 2, 3 y 4 son códigos C puros que utilizan los tipos de Foundation Core, por lo que ARC no tendrá nada que hacer ni se quejará de ellos.
  • En la línea 5, le decimos a ARC que SecItemCopyMatching ha creado su resultado con un conteo de retención de 1, que somos responsables de liberar. (Sabemos esto porque tiene "Copia" en su nombre). __bridge_transfer le informa a ARC sobre esta responsabilidad, por lo que podrá hacerlo automáticamente.
  • No copie el diccionario inmutable de la Base principal devuelto por SecItemCopyMatching al NSMutableDictionary; eso está mal. Además, va en contra de las convenciones generales de estilo de cacao que buildSearchQuery devuelve NSMutableDictionary. Simple NSDictionary s funcionaría bien en ambos casos.

La regla de oro aquí es que __bridge_retained necesidades que deben seguirse por un CFRelease, mientras que el resultado de una función "Crear" "Copiar" o debe ser echado en Cacao-tierra usando __bridge_transfer.

+0

me puede aconsejar algo con respecto a la misma pregunta aquí http://stackoverflow.com/questions/16780202/secitemcopymatching-still-leak-on-osx-under-arc – user170317

0

Método 2: cuando lo usa una vez, ¿por qué necesita una retención o transferencia? ejemplo del trabajo inferior a simple vista para mí, pruebas, debuging (memleaks) todos pasan :) Sólo una cosa goteaba variable de reiniciada automáticamente inédito cuando se encontró clave()

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

método 3: Que hacer ARC el trabajo pesado (o una combinación de los métodos 1 y 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 necesitan poseer el diccionario entrante, por lo __bridge es adecuada y luego ARC sigue para gestionar el tiempo de vida de consulta.

Y mediante la transferencia de la propiedad del resultado de arco, que se encargará de la vida útil de resultado así y nos libera de la necesidad de recordar a CFRelease en todos los códigos caminos.