0

我最近将一个庞大的库迁移到 ARC,无工具部分令人头疼。这是代码:

+ (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 对象被过度释放,我不知道为什么,堆栈跟踪是this。如果我只是设置passwordData并且nil不执行__bridge__transfer,它不会崩溃。关于为什么的任何想法?

非常感谢!

4

3 回答 3

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 对象设置为 nil,除非你已经用 Core Foundation 明确地释放了它,或者将所有权转移给了 ARC。

您可以选择的另一个选择是CFRelease(data)在您返回之前使用普通的__bridge.

这一切都是基于这样的假设,SecItemCopyMatching即为您提供数据的副本并排除您发布它的情况。New并且Copy是通常表明这一点的关键字。您可以通过CFGetRetainCount(data)在不同点使用来验证计数来进一步调试。

我还注意到 fullIdentifier 没有被使用。这是整个功能吗?

您也可以使用initWithData:encoding:代替initWithBytes:length:encoding.

于 2013-03-13T16:43:43.167 回答
0

您需要强制CFDataRef 转换才能NSData尝试使用

 NSData *passwordData = (NSData *)data;

或者只是使用桥而不转移所有权

NSData* passwordData = (__bridge NSData*) data;
于 2013-03-13T16:43:50.050 回答
0

原来问题完全出在另一个对象上,附上正确的代码:

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

在尝试了所有解决方案后,开始阅读SecItemCopyMatchingARC 下方法的错误并得到这个答案

于 2013-03-13T18:29:10.397 回答