2013-03-13 3 views
0

Недавно я перенес огромную библиотеку в ARC, а раздел без инструментов дал головную боль. Вот код:Tall-Bridge overrelease

+ (NSString *)getKeychainItem:(NSString *)identifier 
{ 
    NSString *fullIdentifier = [NSString stringWithFormat:@"%@%@", kIdentifierPrefix, identifier]; 

    NSMutableDictionary *queryKeychain; 
    OSStatus status = noErr; 

    queryKeychain = [NSMutableDictionary dictionary]; 

    // Set the public key query dictionary. 
    [queryKeychain setObject:(__bridge id)kSecClassGenericPassword 
         forKey:(__bridge id)kSecClass]; 

    // Get the key. 
    CFDataRef data; 
    CFDictionaryRef queryKeychainCF = (__bridge CFDictionaryRef)queryKeychain; 
    status = SecItemCopyMatching(queryKeychainCF, (CFTypeRef *)&data); 

    NSData *passwordData = (__bridge_transfer NSData *)data; 

    NSString *password; 

    if (status == noErr) 
    { 
     password = [[NSString alloc] initWithBytes:[passwordData bytes] 
              length:[passwordData length] 
              encoding:NSUTF8StringEncoding]; 

    } 
    else if (status != errSecItemNotFound) 
    { 
     NSLog(@"Error getting keychain item %@ -- OSStatus: %lu", identifier, status); 
    } 

    return password; 
} 

Это должно быть довольно прямо вперед, однако, passwordData объект быть overreleased, и я понятия не имею, почему, трассировки стека является this. Если я просто установил passwordData в nil и не делал __bridge__transfer, это не сбой. Любая идея о том, почему?

Большое спасибо!

ответ

0

Оказывается, проблема была на другом объекте в целом, прикрепляя правильный код :

CFDictionaryRef queryKeychainCF = (__bridge_retained CFDictionaryRef)queryKeychain; 
status = SecItemCopyMatching(queryKeychainCF, (CFTypeRef *)&data); 
NSData *passwordData = (__bridge_transfer NSData *)data; 

A fter пытается все решения, начал читать ошибки для метода SecItemCopyMatching под ARC и добрался до этого answer.

2

Я не использовал __bridge_transfer себя, но если вы измените "passwordData" к этому:

NSData *passwordData = (NSData *)data; 

XCode дает две рекомендации.

Не передавать права собственности (основной фонд должен освободить его):

NSData *passwordData = (__bridge NSData *)data; 

Передача права собственности (ARC берет на себя):

NSData *passwordData = (NSData *)CFBridgingRelease(data); 

__bridge_transfer может быть то же самое, но я у меня не возникло проблем с использованием вызова CFBridgingRelease, что и рекомендует XCode.

Установка ничего в nil фактически не будет выпускать, если ARC не управляет памятью. Вы никогда не хотите, чтобы объект Core Foundation был равен нулю, если вы явно не выпустили его с Core Foundation или не передали право собственности на ARC.

Другой вариант, который у вас есть, это сделать CFRelease(data) непосредственно перед вашим возвратом и просто использовать обычный __bridge.

Все это основано на предположении, что SecItemCopyMatching дает вам копию данных и за исключением вас, чтобы выпустить его. New и Copy - это ключевые слова, которые обычно указывают на это. Вы можете дополнительно отладить, используя CFGetRetainCount(data) в разных точках, чтобы проверить количество.

Я также заметил, что fullIdentifier не используется. Это целая функция?

Вы также можете использовать initWithData:encoding: вместо initWithBytes:length:encoding.

+0

Боюсь, что 'CFBridgingRelease' также не работает: http://i.imgur.com/8Wi4gB7.png.Я начинаю думать, что 'SecItemCopyMatching' запахи ... –

+0

Попробуйте сделать обычный' __bridge' и поместите NSLog (@ "% d", CFGetRetainCount (данные)) в разных местах. NSString может предполагать право собственности на память. – Luke

+0

Спасибо luke, но проблема была другая, связанная с поведением SecItemCopyMatching –

0

Вы должны бросить CFDataRef в NSData попробовать использовать

NSData *passwordData = (NSData *)data; 

или просто использовать мост без передачи права собственности

NSData* passwordData = (__bridge NSData*) data;